diff --git a/.changeset/config.yaml b/.changeset/config.yaml index 38f122c950..412bf42d35 100644 --- a/.changeset/config.yaml +++ b/.changeset/config.yaml @@ -66,6 +66,8 @@ modules: description: Subspaces - id: x/posts description: Posts + - id: x/reports + description: Reports - id: x/fees description: Fees - id: x/supply diff --git a/.changeset/entries/8e3d1d3f5bce775b80851d2757f6de108f5f76c6636be2235359c8b0c2306ffe.yaml b/.changeset/entries/8e3d1d3f5bce775b80851d2757f6de108f5f76c6636be2235359c8b0c2306ffe.yaml new file mode 100644 index 0000000000..d62f8c91c4 --- /dev/null +++ b/.changeset/entries/8e3d1d3f5bce775b80851d2757f6de108f5f76c6636be2235359c8b0c2306ffe.yaml @@ -0,0 +1,6 @@ +type: feat +module: x/reports +pull_request: 860 +description: Added the new `x/reports` module +backward_compatible: true +date: 2022-05-30T09:02:36.66780769Z diff --git a/app/app.go b/app/app.go index fab568b6e0..aabdb5b87b 100644 --- a/app/app.go +++ b/app/app.go @@ -104,6 +104,9 @@ import ( profileskeeper "github.com/desmos-labs/desmos/v3/x/profiles/keeper" profilestypes "github.com/desmos-labs/desmos/v3/x/profiles/types" relationshipskeeper "github.com/desmos-labs/desmos/v3/x/relationships/keeper" + "github.com/desmos-labs/desmos/v3/x/reports" + reportskeeper "github.com/desmos-labs/desmos/v3/x/reports/keeper" + reportstypes "github.com/desmos-labs/desmos/v3/x/reports/types" "github.com/desmos-labs/desmos/v3/x/subspaces" subspaceskeeper "github.com/desmos-labs/desmos/v3/x/subspaces/keeper" @@ -239,6 +242,7 @@ var ( relationships.AppModuleBasic{}, subspaces.AppModuleBasic{}, posts.AppModuleBasic{}, + reports.AppModuleBasic{}, fees.AppModuleBasic{}, supply.AppModuleBasic{}, ) @@ -305,6 +309,7 @@ type DesmosApp struct { ProfileKeeper profileskeeper.Keeper RelationshipsKeeper relationshipskeeper.Keeper PostsKeeper postskeeper.Keeper + ReportsKeeper reportskeeper.Keeper SupplyKeeper supplykeeper.Keeper // Module Manager @@ -353,7 +358,7 @@ func NewDesmosApp( // Custom modules profilestypes.StoreKey, relationshipstypes.StoreKey, subspacestypes.StoreKey, - poststypes.StoreKey, + poststypes.StoreKey, reportstypes.StoreKey, feestypes.StoreKey, ) tkeys := sdk.NewTransientStoreKeys(paramstypes.TStoreKey) @@ -486,7 +491,7 @@ func NewDesmosApp( profilesIBCModule := profiles.NewIBCModule(app.appCodec, app.ProfileKeeper) // Create posts keeper and module - app.PostsKeeper = postskeeper.NewKeeper( + postsKeeper := postskeeper.NewKeeper( app.appCodec, keys[poststypes.StoreKey], app.GetSubspace(poststypes.ModuleName), @@ -494,12 +499,31 @@ func NewDesmosApp( app.RelationshipsKeeper, ) + // Create reports keeper + app.ReportsKeeper = reportskeeper.NewKeeper( + app.appCodec, + keys[reportstypes.StoreKey], + app.GetSubspace(reportstypes.ModuleName), + &subspacesKeeper, + app.RelationshipsKeeper, + &postsKeeper, + ) + + // Register the posts hooks + // NOTE: postsKeeper above is passed by reference, so that it will contain these hooks + app.PostsKeeper = *postsKeeper.SetHooks( + poststypes.NewMultiPostsHooks( + app.ReportsKeeper.Hooks(), + ), + ) + // Register the subspaces hooks // NOTE: subspacesKeeper above is passed by reference, so that it will contain these hooks app.SubspacesKeeper = *subspacesKeeper.SetHooks( subspacestypes.NewMultiSubspacesHooks( app.RelationshipsKeeper.Hooks(), app.PostsKeeper.Hooks(), + app.ReportsKeeper.Hooks(), ), ) @@ -606,6 +630,7 @@ func NewDesmosApp( profilesModule, relationships.NewAppModule(appCodec, app.RelationshipsKeeper, app.SubspacesKeeper, profilesv4.NewKeeper(keys[profilestypes.StoreKey], appCodec), app.AccountKeeper, app.BankKeeper, app.FeesKeeper), posts.NewAppModule(appCodec, app.PostsKeeper, app.SubspacesKeeper, app.AccountKeeper, app.BankKeeper, app.FeesKeeper), + reports.NewAppModule(appCodec, app.ReportsKeeper, app.SubspacesKeeper, app.PostsKeeper, app.AccountKeeper, app.BankKeeper, app.FeesKeeper), supply.NewAppModule(appCodec, legacyAmino, app.SupplyKeeper), ) @@ -640,6 +665,7 @@ func NewDesmosApp( relationshipstypes.ModuleName, profilestypes.ModuleName, poststypes.ModuleName, + reportstypes.ModuleName, supplytypes.ModuleName, ) app.mm.SetOrderEndBlockers( @@ -668,6 +694,7 @@ func NewDesmosApp( relationshipstypes.ModuleName, profilestypes.ModuleName, poststypes.ModuleName, + reportstypes.ModuleName, supplytypes.ModuleName, ) @@ -703,6 +730,7 @@ func NewDesmosApp( profilestypes.ModuleName, relationshipstypes.ModuleName, poststypes.ModuleName, + reportstypes.ModuleName, supplytypes.ModuleName, crisistypes.ModuleName, @@ -737,6 +765,7 @@ func NewDesmosApp( relationshipstypes.ModuleName, profilestypes.ModuleName, poststypes.ModuleName, + reportstypes.ModuleName, supplytypes.ModuleName, crisistypes.ModuleName, @@ -780,6 +809,7 @@ func NewDesmosApp( profilesModule, relationships.NewAppModule(appCodec, app.RelationshipsKeeper, app.SubspacesKeeper, profilesv4.NewKeeper(keys[profilestypes.StoreKey], appCodec), app.AccountKeeper, app.BankKeeper, app.FeesKeeper), posts.NewAppModule(appCodec, app.PostsKeeper, app.SubspacesKeeper, app.AccountKeeper, app.BankKeeper, app.FeesKeeper), + reports.NewAppModule(appCodec, app.ReportsKeeper, app.SubspacesKeeper, app.PostsKeeper, app.AccountKeeper, app.BankKeeper, app.FeesKeeper), ) app.sm.RegisterStoreDecoders() @@ -958,7 +988,7 @@ func (app *DesmosApp) RegisterAPIRoutes(apiSvr *api.Server, apiConfig config.API // register swagger API from root so that other applications can override easily if apiConfig.Swagger { - RegisterSwaggerAPI(clientCtx, apiSvr.Router) + RegisterSwaggerAPI(apiSvr.Router) } } @@ -994,7 +1024,7 @@ func (app *DesmosApp) registerUpgrade(upgrade upgrades.Upgrade) { } // RegisterSwaggerAPI registers swagger route with API Server -func RegisterSwaggerAPI(ctx client.Context, rtr *mux.Router) { +func RegisterSwaggerAPI(rtr *mux.Router) { statikFS, err := fs.New() if err != nil { panic(err) @@ -1033,6 +1063,7 @@ func initParamsKeeper(appCodec codec.BinaryCodec, legacyAmino *codec.LegacyAmino paramsKeeper.Subspace(subspacestypes.ModuleName) paramsKeeper.Subspace(profilestypes.ModuleName) paramsKeeper.Subspace(poststypes.ModuleName) + paramsKeeper.Subspace(reportstypes.ModuleName) return paramsKeeper } diff --git a/app/params/weights.go b/app/params/weights.go index b940598b2b..1f96b75ff7 100644 --- a/app/params/weights.go +++ b/app/params/weights.go @@ -36,4 +36,10 @@ const ( DefaultWeightMsgAddPostAttachment int = 50 DefaultWeightMsgRemovePostAttachment int = 50 DefaultWeightMsgAnswerPoll int = 50 + + DefaultWeightMsgCreateReport int = 50 + DefaultWeightMsgDeleteReport int = 35 + DefaultWeightMsgSupportStandardReason int = 20 + DefaultWeightMsgAddReason int = 10 + DefaultWeightMsgRemoveReason int = 10 ) diff --git a/app/sim_test.go b/app/sim_test.go index a8206d3c75..cbfe33c6b1 100644 --- a/app/sim_test.go +++ b/app/sim_test.go @@ -9,17 +9,15 @@ import ( "path/filepath" "testing" - poststypes "github.com/desmos-labs/desmos/v3/x/posts/types" - feestypes "github.com/desmos-labs/desmos/v3/x/fees/types" - + poststypes "github.com/desmos-labs/desmos/v3/x/posts/types" relationshipstypes "github.com/desmos-labs/desmos/v3/x/relationships/types" + reportstypes "github.com/desmos-labs/desmos/v3/x/reports/types" + subspacestypes "github.com/desmos-labs/desmos/v3/x/subspaces/types" authzkeeper "github.com/cosmos/cosmos-sdk/x/authz/keeper" minttypes "github.com/cosmos/cosmos-sdk/x/mint/types" - subspacestypes "github.com/desmos-labs/desmos/v3/x/subspaces/types" - banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" distrtypes "github.com/cosmos/cosmos-sdk/x/distribution/types" evidencetypes "github.com/cosmos/cosmos-sdk/x/evidence/types" @@ -255,6 +253,7 @@ func TestAppImportExport(t *testing.T) { {app.keys[profilestypes.StoreKey], newApp.keys[profilestypes.StoreKey], [][]byte{}}, {app.keys[relationshipstypes.StoreKey], newApp.keys[relationshipstypes.StoreKey], [][]byte{}}, {app.keys[poststypes.StoreKey], newApp.keys[poststypes.StoreKey], [][]byte{}}, + {app.keys[reportstypes.StoreKey], newApp.keys[reportstypes.StoreKey], [][]byte{}}, } for _, skp := range storeKeysPrefixes { diff --git a/client/docs/config.json b/client/docs/config.json index 9f70371019..3a6b939e92 100644 --- a/client/docs/config.json +++ b/client/docs/config.json @@ -28,6 +28,14 @@ } } }, + { + "url": "./tmp-swagger-gen/desmos/reports/v1/query.swagger.json", + "operationIds": { + "rename": { + "Params": "ReportsParams" + } + } + }, { "url": "./tmp-swagger-gen/desmos/fees/v1/query.swagger.json", "operationIds": { diff --git a/client/docs/swagger-ui/swagger.yaml b/client/docs/swagger-ui/swagger.yaml index ff84df6e1b..02d8709cca 100644 --- a/client/docs/swagger-ui/swagger.yaml +++ b/client/docs/swagger-ui/swagger.yaml @@ -7378,10 +7378,10 @@ paths: format: boolean tags: - Query - /desmos/fees/v1/params: + /desmos/reports/v1/params: get: - summary: Params queries the fees module params - operationId: FeesParams + summary: Params allows to query the module parameters + operationId: ReportsParams responses: '200': description: A successful response. @@ -7391,82 +7391,36 @@ paths: params: type: object properties: - min_fees: + standard_reasons: type: array items: type: object properties: - message_type: + id: + type: integer + format: int64 + title: Id of the reason inside the subspace + title: type: string - amount: - type: array - items: - type: object - properties: - denom: - type: string - amount: - type: string - description: >- - Coin defines a token with a denomination and an - amount. - - - NOTE: The amount field is an Int which implements - the custom method - - signatures required by gogoproto. + title: Title of the reason + description: + type: string + title: >- + (optional) Extended description of the reason and + the cases it applies to title: >- - MinFee contains the minimum amount of coins that should - be paid as a fee for + StandardReason contains the data of a standard reason + that can be picked and - each message of the specific type sent - title: Params contains the parameters for the fees module - title: QueryParamsResponse is the response type for the Query/Params RPC - default: - description: An unexpected error response - schema: - type: object - properties: - error: - type: string - code: - type: integer - format: int32 - message: - type: string - details: - type: array - items: - type: object - properties: - type_url: - type: string - value: - type: string - format: byte - tags: - - Query - '/desmos/supply/v1/circulating/{denom}': - get: - summary: >- - Circulating queries the amount of tokens circulating in the market of - the + used from different subspaces + title: >- + List of available reasons from which new subspaces can + pick their default - given denom - operationId: Circulating - responses: - '200': - description: A successful response. - schema: - type: object - properties: - circulating_supply: - type: string + ones + title: Params contains the module parameters title: >- - QueryCirculatingResponse is the response type for the - Query/Circulating RPC - + QueryParamsResponse is the response type for Query/Params RPC method default: description: An unexpected error response @@ -7657,38 +7611,69 @@ paths: "@type": "type.googleapis.com/google.protobuf.Duration", "value": "1.212s" } - parameters: - - name: denom - description: coin denom to query the circulating supply for - in: path - required: true - type: string - - name: divider_exponent - description: >- - divider_exponent is a factor used to power the divider used to - convert the - - supply to the desired representation. - in: query - required: false - type: string - format: uint64 tags: - Query - '/desmos/supply/v1/total/{denom}': + '/desmos/reports/v1/{subspace_id}/reasons': get: - summary: Total queries the total supply of the given denom - operationId: Total + summary: Reasons allows to query the supported reporting reasons for a subspace + operationId: Reasons responses: '200': description: A successful response. schema: type: object properties: - total_supply: - type: string + reasons: + type: array + items: + type: object + properties: + subspace_id: + type: string + format: uint64 + title: Id of the subspace for which this reason is valid + id: + type: integer + format: int64 + title: Id of the reason inside the subspace + title: + type: string + title: Title of the reason + description: + type: string + title: >- + (optional) Extended description of the reason and the + cases it applies to + title: Reason contains the data about a reporting reason + pagination: + type: object + properties: + next_key: + type: string + format: byte + title: |- + next_key is the key to be passed to PageRequest.key to + query the next page most efficiently + total: + type: string + format: uint64 + title: >- + total is total number of results available if + PageRequest.count_total + + was set, its value is undefined otherwise + description: >- + PageResponse is to be embedded in gRPC response messages where + the + + corresponding request message has used PageRequest. + + message SomeResponse { + repeated Bar results = 1; + PageResponse page = 2; + } title: >- - QueryTotalResponse is the response type for the Query/Total RPC + QueryReasonsResponse is the response type for Query/Reasons RPC method default: description: An unexpected error response @@ -7880,2523 +7865,2188 @@ paths: "value": "1.212s" } parameters: - - name: denom - description: coin denom to query the circulating supply for + - name: subspace_id + description: Id of the subspace to query the supported reporting reasons for in: path required: true type: string - - name: divider_exponent + format: uint64 + - name: pagination.key + description: |- + key is a value returned in PageResponse.next_key to begin + querying the next page most efficiently. Only one of offset or key + should be set. + in: query + required: false + type: string + format: byte + - name: pagination.offset description: >- - divider_exponent is a factor used to power the divider used to - convert the + offset is a numeric offset that can be used when key is unavailable. - supply to the desired representation. + It is less efficient than using key. Only one of offset or key + should + + be set. in: query required: false type: string format: uint64 - tags: - - Query -definitions: - cosmos.base.query.v1beta1.PageRequest: - type: object - properties: - key: - type: string - format: byte - description: |- - key is a value returned in PageResponse.next_key to begin - querying the next page most efficiently. Only one of offset or key - should be set. - offset: - type: string - format: uint64 - description: |- - offset is a numeric offset that can be used when key is unavailable. - It is less efficient than using key. Only one of offset or key should - be set. - limit: - type: string - format: uint64 - description: >- - limit is the total number of results to be returned in the result - page. + - name: pagination.limit + description: >- + limit is the total number of results to be returned in the result + page. - If left empty it will default to a value to be set by each app. - count_total: - type: boolean - format: boolean - description: >- - count_total is set to true to indicate that the result set should - include + If left empty it will default to a value to be set by each app. + in: query + required: false + type: string + format: uint64 + - name: pagination.count_total + description: >- + count_total is set to true to indicate that the result set should + include - a count of the total number of items available for pagination in UIs. + a count of the total number of items available for pagination in + UIs. - count_total is only respected when offset is used. It is ignored when - key + count_total is only respected when offset is used. It is ignored + when key - is set. - reverse: - type: boolean - format: boolean - description: >- - reverse is set to true if results are to be returned in the descending - order. + is set. + in: query + required: false + type: boolean + format: boolean + - name: pagination.reverse + description: >- + reverse is set to true if results are to be returned in the + descending order. - Since: cosmos-sdk 0.43 - description: |- - message SomeRequest { - Foo some_parameter = 1; - PageRequest pagination = 2; - } - title: |- - PageRequest is to be embedded in gRPC request messages for efficient - pagination. Ex: - cosmos.base.query.v1beta1.PageResponse: - type: object - properties: - next_key: - type: string - format: byte - title: |- - next_key is the key to be passed to PageRequest.key to - query the next page most efficiently - total: - type: string - format: uint64 - title: |- - total is total number of results available if PageRequest.count_total - was set, its value is undefined otherwise - description: |- - PageResponse is to be embedded in gRPC response messages where the - corresponding request message has used PageRequest. - - message SomeResponse { - repeated Bar results = 1; - PageResponse page = 2; - } - cosmos.base.v1beta1.Coin: - type: object - properties: - denom: - type: string - amount: - type: string - description: |- - Coin defines a token with a denomination and an amount. - - NOTE: The amount field is an Int which implements the custom method - signatures required by gogoproto. - desmos.profiles.v2.ApplicationLink: - type: object - properties: - user: - type: string - title: User to which the link is associated - data: - title: Data contains the details of this specific link - type: object - properties: - application: - type: string - title: 'The application name (eg. Twitter, GitHub, etc)' - username: - type: string - title: 'Username on the application (eg. Twitter tag, GitHub profile, etc)' - state: - title: State of the link - type: string - enum: - - APPLICATION_LINK_STATE_INITIALIZED_UNSPECIFIED - - APPLICATION_LINK_STATE_VERIFICATION_STARTED - - APPLICATION_LINK_STATE_VERIFICATION_ERROR - - APPLICATION_LINK_STATE_VERIFICATION_SUCCESS - - APPLICATION_LINK_STATE_TIMED_OUT - default: APPLICATION_LINK_STATE_INITIALIZED_UNSPECIFIED - description: >- - - APPLICATION_LINK_STATE_INITIALIZED_UNSPECIFIED: A link has just been - initialized - - APPLICATION_LINK_STATE_VERIFICATION_STARTED: A link has just started being verified - - APPLICATION_LINK_STATE_VERIFICATION_ERROR: A link has errored during the verification process - - APPLICATION_LINK_STATE_VERIFICATION_SUCCESS: A link has being verified successfully - - APPLICATION_LINK_STATE_TIMED_OUT: A link has timed out while waiting for the verification - oracle_request: - title: OracleRequest represents the request that has been made to the oracle - type: object - properties: - id: - type: string - format: uint64 - title: ID is the ID of the request - oracle_script_id: - type: string - format: uint64 - title: OracleScriptID is ID of an oracle script - call_data: - title: CallData contains the data used to perform the oracle request - type: object - properties: - application: - type: string - title: The application for which the ownership should be verified - call_data: - type: string - title: >- - The hex encoded call data that should be used to verify the - application - - account ownership - client_id: - type: string - title: >- - ClientID represents the ID of the client that has called the - oracle script - result: - title: |- - Data coming from the result of the verification. - Only available when the state is STATE_SUCCESS - type: object - properties: - success: - title: Success represents a successful verification - type: object - properties: - value: - type: string - title: Hex-encoded value that has be signed by the profile - signature: - type: string - title: >- - Hex-encoded signature that has been produced by signing the - value - failed: - title: Failed represents a failed verification + Since: cosmos-sdk 0.43 + in: query + required: false + type: boolean + format: boolean + tags: + - Query + '/desmos/reports/v1/{subspace_id}/reports': + get: + summary: Reports allows to query the reports for a specific target + operationId: Reports + responses: + '200': + description: A successful response. + schema: type: object properties: - error: - type: string - title: Error that is associated with the failure - creation_time: - type: string - format: date-time - title: CreationTime represents the time in which the link was created - title: ApplicationLink contains the data of a link to a centralized application - desmos.profiles.v2.ApplicationLinkState: - type: string - enum: - - APPLICATION_LINK_STATE_INITIALIZED_UNSPECIFIED - - APPLICATION_LINK_STATE_VERIFICATION_STARTED - - APPLICATION_LINK_STATE_VERIFICATION_ERROR - - APPLICATION_LINK_STATE_VERIFICATION_SUCCESS - - APPLICATION_LINK_STATE_TIMED_OUT - default: APPLICATION_LINK_STATE_INITIALIZED_UNSPECIFIED - description: >- - - APPLICATION_LINK_STATE_INITIALIZED_UNSPECIFIED: A link has just been - initialized - - APPLICATION_LINK_STATE_VERIFICATION_STARTED: A link has just started being verified - - APPLICATION_LINK_STATE_VERIFICATION_ERROR: A link has errored during the verification process - - APPLICATION_LINK_STATE_VERIFICATION_SUCCESS: A link has being verified successfully - - APPLICATION_LINK_STATE_TIMED_OUT: A link has timed out while waiting for the verification - title: |- - ApplicationLinkState defines if an application link is in the following - states: STARTED, ERRORED, SUCCESSFUL, TIMED_OUT - desmos.profiles.v2.BioParams: - type: object - properties: - max_length: - type: string - format: byte - title: BioParams defines the parameters related to profile biography - desmos.profiles.v2.ChainConfig: - type: object - properties: - name: - type: string - description: ChainConfig contains the data of the chain with which the link is made. - desmos.profiles.v2.ChainLink: - type: object - properties: - user: - type: string - title: User defines the destination profile address to link - address: - type: object - properties: - type_url: - type: string - description: >- - A URL/resource name that uniquely identifies the type of the - serialized + reports: + type: array + items: + type: object + properties: + subspace_id: + type: string + format: uint64 + title: Id of the subspace for which the report has been created + id: + type: string + format: uint64 + title: Id of the report + reasons_ids: + type: array + items: + type: integer + format: int64 + title: Id of the reason this report has been created for + message: + type: string + title: (optional) Message attached to this report + reporter: + type: string + title: Address of the reporter + target: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the + type of the serialized - protocol buffer message. This string must contain at least + protocol buffer message. This string must contain at + least - one "/" character. The last segment of the URL's path must - represent + one "/" character. The last segment of the URL's + path must represent - the fully qualified name of the type (as in + the fully qualified name of the type (as in - `path/google.protobuf.Duration`). The name should be in a - canonical form + `path/google.protobuf.Duration`). The name should be + in a canonical form - (e.g., leading "." is not accepted). + (e.g., leading "." is not accepted). - In practice, teams usually precompile into the binary all types - that they + In practice, teams usually precompile into the + binary all types that they - expect it to use in the context of Any. However, for URLs which - use the + expect it to use in the context of Any. However, for + URLs which use the - scheme `http`, `https`, or no scheme, one can optionally set up a - type + scheme `http`, `https`, or no scheme, one can + optionally set up a type - server that maps type URLs to message definitions as follows: + server that maps type URLs to message definitions as + follows: - * If no scheme is provided, `https` is assumed. + * If no scheme is provided, `https` is assumed. - * An HTTP GET on the URL must yield a [google.protobuf.Type][] - value in binary format, or produce an error. - * Applications are allowed to cache lookup results based on the - URL, or have them precompiled into a binary to avoid any - lookup. Therefore, binary compatibility needs to be preserved - on changes to types. (Use versioned type names to manage - breaking changes.) + * An HTTP GET on the URL must yield a + [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results + based on the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) - Note: this functionality is not currently available in the - official + Note: this functionality is not currently available + in the official - protobuf release, and it is not used for type URLs beginning with + protobuf release, and it is not used for type URLs + beginning with - type.googleapis.com. + type.googleapis.com. - Schemes other than `http`, `https` (or the empty scheme) might be + Schemes other than `http`, `https` (or the empty + scheme) might be - used with implementation specific semantics. - value: - type: string - format: byte - description: >- - Must be a valid serialized protocol buffer of the above specified - type. - description: >- - `Any` contains an arbitrary serialized protocol buffer message along - with a + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the + above specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer + message along with a - URL that describes the type of the serialized message. + URL that describes the type of the serialized message. - Protobuf library provides support to pack/unpack Any values in the - form + Protobuf library provides support to pack/unpack Any + values in the form - of utility functions or additional generated methods of the Any type. + of utility functions or additional generated methods of + the Any type. - Example 1: Pack and unpack a message in C++. + Example 1: Pack and unpack a message in C++. - Foo foo = ...; - Any any; - any.PackFrom(foo); - ... - if (any.UnpackTo(&foo)) { - ... - } + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } - Example 2: Pack and unpack a message in Java. + Example 2: Pack and unpack a message in Java. - Foo foo = ...; - Any any = Any.pack(foo); - ... - if (any.is(Foo.class)) { - foo = any.unpack(Foo.class); - } + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } - Example 3: Pack and unpack a message in Python. + Example 3: Pack and unpack a message in Python. - foo = Foo(...) - any = Any() - any.Pack(foo) - ... - if any.Is(Foo.DESCRIPTOR): - any.Unpack(foo) - ... + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... - Example 4: Pack and unpack a message in Go + Example 4: Pack and unpack a message in Go - foo := &pb.Foo{...} - any, err := ptypes.MarshalAny(foo) - ... - foo := &pb.Foo{} - if err := ptypes.UnmarshalAny(any, foo); err != nil { - ... - } + foo := &pb.Foo{...} + any, err := ptypes.MarshalAny(foo) + ... + foo := &pb.Foo{} + if err := ptypes.UnmarshalAny(any, foo); err != nil { + ... + } - The pack methods provided by protobuf library will by default use + The pack methods provided by protobuf library will by + default use - 'type.googleapis.com/full.type.name' as the type URL and the unpack + 'type.googleapis.com/full.type.name' as the type URL and + the unpack - methods only use the fully qualified type name after the last '/' + methods only use the fully qualified type name after the + last '/' - in the type URL, for example "foo.bar.com/x/y.z" will yield type + in the type URL, for example "foo.bar.com/x/y.z" will + yield type - name "y.z". + name "y.z". - JSON + JSON - ==== + ==== - The JSON representation of an `Any` value uses the regular + The JSON representation of an `Any` value uses the + regular - representation of the deserialized, embedded message, with an + representation of the deserialized, embedded message, + with an - additional field `@type` which contains the type URL. Example: + additional field `@type` which contains the type URL. + Example: - package google.profile; - message Person { - string first_name = 1; - string last_name = 2; - } + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } - { - "@type": "type.googleapis.com/google.profile.Person", - "firstName": , - "lastName": - } + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } - If the embedded message type is well-known and has a custom JSON + If the embedded message type is well-known and has a + custom JSON - representation, that representation will be embedded adding a field + representation, that representation will be embedded + adding a field - `value` which holds the custom JSON in addition to the `@type` + `value` which holds the custom JSON in addition to the + `@type` - field. Example (for message [google.protobuf.Duration][]): + field. Example (for message + [google.protobuf.Duration][]): - { - "@type": "type.googleapis.com/google.protobuf.Duration", - "value": "1.212s" - } - title: >- - Address contains the data of the external chain address to be - connected + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + title: Target of the report + creation_date: + type: string + format: date-time + title: Time in which the report was created + title: Report contains the data of a generic report + pagination: + type: object + properties: + next_key: + type: string + format: byte + title: |- + next_key is the key to be passed to PageRequest.key to + query the next page most efficiently + total: + type: string + format: uint64 + title: >- + total is total number of results available if + PageRequest.count_total - with the Desmos profile - proof: - title: Proof contains the ownership proof of the external chain address - type: object - properties: - pub_key: + was set, its value is undefined otherwise + description: >- + PageResponse is to be embedded in gRPC response messages where + the + + corresponding request message has used PageRequest. + + message SomeResponse { + repeated Bar results = 1; + PageResponse page = 2; + } + title: >- + QueryReportsResponse is the response type for Query/Reports RPC + method + default: + description: An unexpected error response + schema: type: object properties: - type_url: + error: type: string - description: >- - A URL/resource name that uniquely identifies the type of the - serialized + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type of + the serialized - protocol buffer message. This string must contain at least + protocol buffer message. This string must contain at + least - one "/" character. The last segment of the URL's path must - represent + one "/" character. The last segment of the URL's path + must represent - the fully qualified name of the type (as in + the fully qualified name of the type (as in - `path/google.protobuf.Duration`). The name should be in a - canonical form + `path/google.protobuf.Duration`). The name should be in + a canonical form - (e.g., leading "." is not accepted). + (e.g., leading "." is not accepted). - In practice, teams usually precompile into the binary all - types that they + In practice, teams usually precompile into the binary + all types that they - expect it to use in the context of Any. However, for URLs - which use the + expect it to use in the context of Any. However, for + URLs which use the - scheme `http`, `https`, or no scheme, one can optionally set - up a type + scheme `http`, `https`, or no scheme, one can optionally + set up a type - server that maps type URLs to message definitions as follows: + server that maps type URLs to message definitions as + follows: - * If no scheme is provided, `https` is assumed. + * If no scheme is provided, `https` is assumed. - * An HTTP GET on the URL must yield a [google.protobuf.Type][] - value in binary format, or produce an error. - * Applications are allowed to cache lookup results based on - the - URL, or have them precompiled into a binary to avoid any - lookup. Therefore, binary compatibility needs to be preserved - on changes to types. (Use versioned type names to manage - breaking changes.) - - Note: this functionality is not currently available in the - official - - protobuf release, and it is not used for type URLs beginning - with - - type.googleapis.com. - - - Schemes other than `http`, `https` (or the empty scheme) might - be - - used with implementation specific semantics. - value: - type: string - format: byte - description: >- - Must be a valid serialized protocol buffer of the above - specified type. - description: >- - `Any` contains an arbitrary serialized protocol buffer message - along with a + * An HTTP GET on the URL must yield a + [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based + on the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) - URL that describes the type of the serialized message. + Note: this functionality is not currently available in + the official + protobuf release, and it is not used for type URLs + beginning with - Protobuf library provides support to pack/unpack Any values in the - form + type.googleapis.com. - of utility functions or additional generated methods of the Any - type. + Schemes other than `http`, `https` (or the empty scheme) + might be - Example 1: Pack and unpack a message in C++. + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above + specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer + message along with a - Foo foo = ...; - Any any; - any.PackFrom(foo); - ... - if (any.UnpackTo(&foo)) { - ... - } + URL that describes the type of the serialized message. - Example 2: Pack and unpack a message in Java. - Foo foo = ...; - Any any = Any.pack(foo); - ... - if (any.is(Foo.class)) { - foo = any.unpack(Foo.class); - } + Protobuf library provides support to pack/unpack Any values + in the form - Example 3: Pack and unpack a message in Python. + of utility functions or additional generated methods of the + Any type. - foo = Foo(...) - any = Any() - any.Pack(foo) - ... - if any.Is(Foo.DESCRIPTOR): - any.Unpack(foo) - ... - Example 4: Pack and unpack a message in Go + Example 1: Pack and unpack a message in C++. - foo := &pb.Foo{...} - any, err := ptypes.MarshalAny(foo) - ... - foo := &pb.Foo{} - if err := ptypes.UnmarshalAny(any, foo); err != nil { - ... - } + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } - The pack methods provided by protobuf library will by default use + Example 2: Pack and unpack a message in Java. - 'type.googleapis.com/full.type.name' as the type URL and the - unpack + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } - methods only use the fully qualified type name after the last '/' + Example 3: Pack and unpack a message in Python. - in the type URL, for example "foo.bar.com/x/y.z" will yield type + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... - name "y.z". + Example 4: Pack and unpack a message in Go + foo := &pb.Foo{...} + any, err := ptypes.MarshalAny(foo) + ... + foo := &pb.Foo{} + if err := ptypes.UnmarshalAny(any, foo); err != nil { + ... + } + The pack methods provided by protobuf library will by + default use - JSON + 'type.googleapis.com/full.type.name' as the type URL and the + unpack - ==== + methods only use the fully qualified type name after the + last '/' - The JSON representation of an `Any` value uses the regular + in the type URL, for example "foo.bar.com/x/y.z" will yield + type - representation of the deserialized, embedded message, with an + name "y.z". - additional field `@type` which contains the type URL. Example: - package google.profile; - message Person { - string first_name = 1; - string last_name = 2; - } - { - "@type": "type.googleapis.com/google.profile.Person", - "firstName": , - "lastName": - } + JSON - If the embedded message type is well-known and has a custom JSON + ==== - representation, that representation will be embedded adding a - field + The JSON representation of an `Any` value uses the regular - `value` which holds the custom JSON in addition to the `@type` + representation of the deserialized, embedded message, with + an - field. Example (for message [google.protobuf.Duration][]): + additional field `@type` which contains the type URL. + Example: - { - "@type": "type.googleapis.com/google.protobuf.Duration", - "value": "1.212s" - } - title: >- - PubKey represents the public key associated with the address for - which to + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } - prove the ownership - signature: - type: object - properties: - type_url: - type: string - description: >- - A URL/resource name that uniquely identifies the type of the - serialized + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } - protocol buffer message. This string must contain at least + If the embedded message type is well-known and has a custom + JSON - one "/" character. The last segment of the URL's path must - represent + representation, that representation will be embedded adding + a field - the fully qualified name of the type (as in + `value` which holds the custom JSON in addition to the + `@type` - `path/google.protobuf.Duration`). The name should be in a - canonical form + field. Example (for message [google.protobuf.Duration][]): - (e.g., leading "." is not accepted). + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + parameters: + - name: subspace_id + description: Id of the subspace to query the reports for + in: path + required: true + type: string + format: uint64 + - name: target.type_url + description: >- + A URL/resource name that uniquely identifies the type of the + serialized + protocol buffer message. This string must contain at least - In practice, teams usually precompile into the binary all - types that they + one "/" character. The last segment of the URL's path must represent - expect it to use in the context of Any. However, for URLs - which use the + the fully qualified name of the type (as in - scheme `http`, `https`, or no scheme, one can optionally set - up a type + `path/google.protobuf.Duration`). The name should be in a canonical + form - server that maps type URLs to message definitions as follows: + (e.g., leading "." is not accepted). - * If no scheme is provided, `https` is assumed. + In practice, teams usually precompile into the binary all types that + they - * An HTTP GET on the URL must yield a [google.protobuf.Type][] - value in binary format, or produce an error. - * Applications are allowed to cache lookup results based on - the - URL, or have them precompiled into a binary to avoid any - lookup. Therefore, binary compatibility needs to be preserved - on changes to types. (Use versioned type names to manage - breaking changes.) + expect it to use in the context of Any. However, for URLs which use + the - Note: this functionality is not currently available in the - official + scheme `http`, `https`, or no scheme, one can optionally set up a + type - protobuf release, and it is not used for type URLs beginning - with + server that maps type URLs to message definitions as follows: - type.googleapis.com. + * If no scheme is provided, `https` is assumed. - Schemes other than `http`, `https` (or the empty scheme) might - be + * An HTTP GET on the URL must yield a [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based on the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) - used with implementation specific semantics. - value: - type: string - format: byte - description: >- - Must be a valid serialized protocol buffer of the above - specified type. - description: >- - `Any` contains an arbitrary serialized protocol buffer message - along with a + Note: this functionality is not currently available in the official - URL that describes the type of the serialized message. + protobuf release, and it is not used for type URLs beginning with + type.googleapis.com. - Protobuf library provides support to pack/unpack Any values in the - form - of utility functions or additional generated methods of the Any - type. + Schemes other than `http`, `https` (or the empty scheme) might be + used with implementation specific semantics. + in: query + required: false + type: string + - name: target.value + description: >- + Must be a valid serialized protocol buffer of the above specified + type. + in: query + required: false + type: string + format: byte + - name: reporter + description: |- + (optional) User that reported the target. + This is going to be used only if the target is also specified. + in: query + required: false + type: string + - name: pagination.key + description: |- + key is a value returned in PageResponse.next_key to begin + querying the next page most efficiently. Only one of offset or key + should be set. + in: query + required: false + type: string + format: byte + - name: pagination.offset + description: >- + offset is a numeric offset that can be used when key is unavailable. - Example 1: Pack and unpack a message in C++. + It is less efficient than using key. Only one of offset or key + should - Foo foo = ...; - Any any; - any.PackFrom(foo); - ... - if (any.UnpackTo(&foo)) { - ... - } + be set. + in: query + required: false + type: string + format: uint64 + - name: pagination.limit + description: >- + limit is the total number of results to be returned in the result + page. - Example 2: Pack and unpack a message in Java. + If left empty it will default to a value to be set by each app. + in: query + required: false + type: string + format: uint64 + - name: pagination.count_total + description: >- + count_total is set to true to indicate that the result set should + include - Foo foo = ...; - Any any = Any.pack(foo); - ... - if (any.is(Foo.class)) { - foo = any.unpack(Foo.class); - } + a count of the total number of items available for pagination in + UIs. - Example 3: Pack and unpack a message in Python. + count_total is only respected when offset is used. It is ignored + when key - foo = Foo(...) - any = Any() - any.Pack(foo) - ... - if any.Is(Foo.DESCRIPTOR): - any.Unpack(foo) - ... + is set. + in: query + required: false + type: boolean + format: boolean + - name: pagination.reverse + description: >- + reverse is set to true if results are to be returned in the + descending order. - Example 4: Pack and unpack a message in Go - foo := &pb.Foo{...} - any, err := ptypes.MarshalAny(foo) - ... - foo := &pb.Foo{} - if err := ptypes.UnmarshalAny(any, foo); err != nil { - ... - } + Since: cosmos-sdk 0.43 + in: query + required: false + type: boolean + format: boolean + tags: + - Query + /desmos/fees/v1/params: + get: + summary: Params queries the fees module params + operationId: FeesParams + responses: + '200': + description: A successful response. + schema: + type: object + properties: + params: + type: object + properties: + min_fees: + type: array + items: + type: object + properties: + message_type: + type: string + amount: + type: array + items: + type: object + properties: + denom: + type: string + amount: + type: string + description: >- + Coin defines a token with a denomination and an + amount. - The pack methods provided by protobuf library will by default use - 'type.googleapis.com/full.type.name' as the type URL and the - unpack + NOTE: The amount field is an Int which implements + the custom method - methods only use the fully qualified type name after the last '/' + signatures required by gogoproto. + title: >- + MinFee contains the minimum amount of coins that should + be paid as a fee for - in the type URL, for example "foo.bar.com/x/y.z" will yield type + each message of the specific type sent + title: Params contains the parameters for the fees module + title: QueryParamsResponse is the response type for the Query/Params RPC + default: + description: An unexpected error response + schema: + type: object + properties: + error: + type: string + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + type_url: + type: string + value: + type: string + format: byte + tags: + - Query + '/desmos/supply/v1/circulating/{denom}': + get: + summary: >- + Circulating queries the amount of tokens circulating in the market of + the - name "y.z". + given denom + operationId: Circulating + responses: + '200': + description: A successful response. + schema: + type: object + properties: + circulating_supply: + type: string + title: >- + QueryCirculatingResponse is the response type for the + Query/Circulating RPC + method + default: + description: An unexpected error response + schema: + type: object + properties: + error: + type: string + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type of + the serialized + protocol buffer message. This string must contain at + least - JSON + one "/" character. The last segment of the URL's path + must represent - ==== + the fully qualified name of the type (as in - The JSON representation of an `Any` value uses the regular + `path/google.protobuf.Duration`). The name should be in + a canonical form - representation of the deserialized, embedded message, with an + (e.g., leading "." is not accepted). - additional field `@type` which contains the type URL. Example: - package google.profile; - message Person { - string first_name = 1; - string last_name = 2; - } + In practice, teams usually precompile into the binary + all types that they - { - "@type": "type.googleapis.com/google.profile.Person", - "firstName": , - "lastName": - } + expect it to use in the context of Any. However, for + URLs which use the - If the embedded message type is well-known and has a custom JSON + scheme `http`, `https`, or no scheme, one can optionally + set up a type - representation, that representation will be embedded adding a - field + server that maps type URLs to message definitions as + follows: - `value` which holds the custom JSON in addition to the `@type` - field. Example (for message [google.protobuf.Duration][]): + * If no scheme is provided, `https` is assumed. - { - "@type": "type.googleapis.com/google.protobuf.Duration", - "value": "1.212s" - } - title: >- - Signature represents the hex-encoded signature of the PlainText - value - plain_text: - type: string - title: >- - PlainText represents the hex-encoded value signed in order to - produce the + * An HTTP GET on the URL must yield a + [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based + on the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) - Signature - chain_config: - title: ChainConfig contains the configuration of the external chain - type: object - properties: - name: - type: string - description: >- - ChainConfig contains the data of the chain with which the link is - made. - creation_time: - type: string - format: date-time - title: CreationTime represents the time in which the link has been created - title: |- - ChainLink contains the data representing either an inter- or cross- chain - link - desmos.profiles.v2.DTagParams: - type: object - properties: - reg_ex: - type: string - min_length: - type: string - format: byte - max_length: - type: string - format: byte - title: DTagParams defines the parameters related to profile DTags - desmos.profiles.v2.DTagTransferRequest: - type: object - properties: - dtag_to_trade: - type: string - title: >- - DTagToTrade contains the value of the DTag that should be transferred - from + Note: this functionality is not currently available in + the official - the receiver of the request to the sender - sender: - type: string - title: Sender represents the address of the account that sent the request - receiver: - type: string - title: >- - Receiver represents the receiver of the request that, if accepted, - will + protobuf release, and it is not used for type URLs + beginning with - give to the sender their DTag - title: DTagTransferRequest represent a DTag transfer request between two users - desmos.profiles.v2.Data: - type: object - properties: - application: - type: string - title: 'The application name (eg. Twitter, GitHub, etc)' - username: - type: string - title: 'Username on the application (eg. Twitter tag, GitHub profile, etc)' - title: |- - Data contains the data associated to a specific user of a - generic centralized application - desmos.profiles.v2.NicknameParams: - type: object - properties: - min_length: - type: string - format: byte - max_length: - type: string - format: byte - title: NicknameParams defines the parameters related to the profiles nicknames - desmos.profiles.v2.OracleParams: - type: object - properties: - script_id: - type: string - format: uint64 - title: >- - ScriptID represents the ID of the oracle script to be called to verify - the + type.googleapis.com. - data - ask_count: - type: string - format: uint64 - title: >- - AskCount represents the number of oracles to which ask to verify the - data - min_count: - type: string - format: uint64 - title: >- - MinCount represents the minimum count of oracles that should complete - the - verification successfully - prepare_gas: - type: string - format: uint64 - title: >- - PrepareGas represents the amount of gas to be used during the - preparation + Schemes other than `http`, `https` (or the empty scheme) + might be - stage of the oracle script - execute_gas: - type: string - format: uint64 - title: >- - ExecuteGas represents the amount of gas to be used during the - execution of + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above + specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer + message along with a - the oracle script - fee_amount: - type: array - items: - type: object - properties: - denom: - type: string - amount: - type: string - description: |- - Coin defines a token with a denomination and an amount. + URL that describes the type of the serialized message. - NOTE: The amount field is an Int which implements the custom method - signatures required by gogoproto. - title: >- - FeeAmount represents the amount of fees to be payed in order to - execute the - oracle script - title: |- - OracleParams defines the parameters related to the oracle - that will be used to verify the ownership of a centralized - application account by a Desmos profile - desmos.profiles.v2.OracleRequest: - type: object - properties: - id: - type: string - format: uint64 - title: ID is the ID of the request - oracle_script_id: - type: string - format: uint64 - title: OracleScriptID is ID of an oracle script - call_data: - title: CallData contains the data used to perform the oracle request - type: object - properties: - application: - type: string - title: The application for which the ownership should be verified - call_data: - type: string - title: >- - The hex encoded call data that should be used to verify the - application + Protobuf library provides support to pack/unpack Any values + in the form - account ownership - client_id: - type: string - title: >- - ClientID represents the ID of the client that has called the oracle - script - title: |- - OracleRequest represents a generic oracle request used to - verify the ownership of a centralized application account - desmos.profiles.v2.OracleRequest.CallData: - type: object - properties: - application: - type: string - title: The application for which the ownership should be verified - call_data: - type: string - title: >- - The hex encoded call data that should be used to verify the - application + of utility functions or additional generated methods of the + Any type. - account ownership - title: |- - CallData contains the data sent to a single oracle request in order to - verify the ownership of a centralized application by a Desmos profile - desmos.profiles.v2.Params: - type: object - properties: - nickname: - type: object - properties: - min_length: - type: string - format: byte - max_length: - type: string - format: byte - title: >- - NicknameParams defines the parameters related to the profiles - nicknames - dtag: - type: object - properties: - reg_ex: - type: string - min_length: - type: string - format: byte - max_length: - type: string - format: byte - title: DTagParams defines the parameters related to profile DTags - bio: - type: object - properties: - max_length: - type: string - format: byte - title: BioParams defines the parameters related to profile biography - oracle: - type: object - properties: - script_id: - type: string - format: uint64 - title: >- - ScriptID represents the ID of the oracle script to be called to - verify the - data - ask_count: - type: string - format: uint64 - title: >- - AskCount represents the number of oracles to which ask to verify - the data - min_count: - type: string - format: uint64 - title: >- - MinCount represents the minimum count of oracles that should - complete the + Example 1: Pack and unpack a message in C++. - verification successfully - prepare_gas: - type: string - format: uint64 - title: >- - PrepareGas represents the amount of gas to be used during the - preparation + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } - stage of the oracle script - execute_gas: - type: string - format: uint64 - title: >- - ExecuteGas represents the amount of gas to be used during the - execution of + Example 2: Pack and unpack a message in Java. - the oracle script - fee_amount: - type: array - items: - type: object - properties: - denom: - type: string - amount: - type: string - description: >- - Coin defines a token with a denomination and an amount. + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + Example 3: Pack and unpack a message in Python. - NOTE: The amount field is an Int which implements the custom - method + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... - signatures required by gogoproto. - title: >- - FeeAmount represents the amount of fees to be payed in order to - execute the + Example 4: Pack and unpack a message in Go - oracle script - title: |- - OracleParams defines the parameters related to the oracle - that will be used to verify the ownership of a centralized - application account by a Desmos profile - title: Params contains the parameters for the profiles module - desmos.profiles.v2.Proof: - type: object - properties: - pub_key: - type: object - properties: - type_url: - type: string - description: >- - A URL/resource name that uniquely identifies the type of the - serialized + foo := &pb.Foo{...} + any, err := ptypes.MarshalAny(foo) + ... + foo := &pb.Foo{} + if err := ptypes.UnmarshalAny(any, foo); err != nil { + ... + } - protocol buffer message. This string must contain at least + The pack methods provided by protobuf library will by + default use - one "/" character. The last segment of the URL's path must - represent + 'type.googleapis.com/full.type.name' as the type URL and the + unpack - the fully qualified name of the type (as in + methods only use the fully qualified type name after the + last '/' - `path/google.protobuf.Duration`). The name should be in a - canonical form + in the type URL, for example "foo.bar.com/x/y.z" will yield + type - (e.g., leading "." is not accepted). + name "y.z". - In practice, teams usually precompile into the binary all types - that they - expect it to use in the context of Any. However, for URLs which - use the + JSON - scheme `http`, `https`, or no scheme, one can optionally set up a - type + ==== - server that maps type URLs to message definitions as follows: + The JSON representation of an `Any` value uses the regular + representation of the deserialized, embedded message, with + an - * If no scheme is provided, `https` is assumed. + additional field `@type` which contains the type URL. + Example: - * An HTTP GET on the URL must yield a [google.protobuf.Type][] - value in binary format, or produce an error. - * Applications are allowed to cache lookup results based on the - URL, or have them precompiled into a binary to avoid any - lookup. Therefore, binary compatibility needs to be preserved - on changes to types. (Use versioned type names to manage - breaking changes.) + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } - Note: this functionality is not currently available in the - official + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } - protobuf release, and it is not used for type URLs beginning with + If the embedded message type is well-known and has a custom + JSON - type.googleapis.com. + representation, that representation will be embedded adding + a field + `value` which holds the custom JSON in addition to the + `@type` - Schemes other than `http`, `https` (or the empty scheme) might be + field. Example (for message [google.protobuf.Duration][]): - used with implementation specific semantics. - value: - type: string - format: byte - description: >- - Must be a valid serialized protocol buffer of the above specified - type. - description: >- - `Any` contains an arbitrary serialized protocol buffer message along - with a + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + parameters: + - name: denom + description: coin denom to query the circulating supply for + in: path + required: true + type: string + - name: divider_exponent + description: >- + divider_exponent is a factor used to power the divider used to + convert the - URL that describes the type of the serialized message. + supply to the desired representation. + in: query + required: false + type: string + format: uint64 + tags: + - Query + '/desmos/supply/v1/total/{denom}': + get: + summary: Total queries the total supply of the given denom + operationId: Total + responses: + '200': + description: A successful response. + schema: + type: object + properties: + total_supply: + type: string + title: >- + QueryTotalResponse is the response type for the Query/Total RPC + method + default: + description: An unexpected error response + schema: + type: object + properties: + error: + type: string + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type of + the serialized + protocol buffer message. This string must contain at + least - Protobuf library provides support to pack/unpack Any values in the - form + one "/" character. The last segment of the URL's path + must represent - of utility functions or additional generated methods of the Any type. + the fully qualified name of the type (as in + `path/google.protobuf.Duration`). The name should be in + a canonical form - Example 1: Pack and unpack a message in C++. + (e.g., leading "." is not accepted). - Foo foo = ...; - Any any; - any.PackFrom(foo); - ... - if (any.UnpackTo(&foo)) { - ... - } - Example 2: Pack and unpack a message in Java. + In practice, teams usually precompile into the binary + all types that they - Foo foo = ...; - Any any = Any.pack(foo); - ... - if (any.is(Foo.class)) { - foo = any.unpack(Foo.class); - } + expect it to use in the context of Any. However, for + URLs which use the - Example 3: Pack and unpack a message in Python. + scheme `http`, `https`, or no scheme, one can optionally + set up a type - foo = Foo(...) - any = Any() - any.Pack(foo) - ... - if any.Is(Foo.DESCRIPTOR): - any.Unpack(foo) - ... + server that maps type URLs to message definitions as + follows: - Example 4: Pack and unpack a message in Go - foo := &pb.Foo{...} - any, err := ptypes.MarshalAny(foo) - ... - foo := &pb.Foo{} - if err := ptypes.UnmarshalAny(any, foo); err != nil { - ... - } + * If no scheme is provided, `https` is assumed. - The pack methods provided by protobuf library will by default use + * An HTTP GET on the URL must yield a + [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based + on the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) - 'type.googleapis.com/full.type.name' as the type URL and the unpack + Note: this functionality is not currently available in + the official - methods only use the fully qualified type name after the last '/' + protobuf release, and it is not used for type URLs + beginning with - in the type URL, for example "foo.bar.com/x/y.z" will yield type + type.googleapis.com. - name "y.z". + Schemes other than `http`, `https` (or the empty scheme) + might be + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above + specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer + message along with a - JSON + URL that describes the type of the serialized message. - ==== - The JSON representation of an `Any` value uses the regular + Protobuf library provides support to pack/unpack Any values + in the form - representation of the deserialized, embedded message, with an + of utility functions or additional generated methods of the + Any type. - additional field `@type` which contains the type URL. Example: - package google.profile; - message Person { - string first_name = 1; - string last_name = 2; - } + Example 1: Pack and unpack a message in C++. - { - "@type": "type.googleapis.com/google.profile.Person", - "firstName": , - "lastName": - } + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } - If the embedded message type is well-known and has a custom JSON + Example 2: Pack and unpack a message in Java. - representation, that representation will be embedded adding a field + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } - `value` which holds the custom JSON in addition to the `@type` + Example 3: Pack and unpack a message in Python. - field. Example (for message [google.protobuf.Duration][]): + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... - { - "@type": "type.googleapis.com/google.protobuf.Duration", - "value": "1.212s" - } - title: >- - PubKey represents the public key associated with the address for which - to + Example 4: Pack and unpack a message in Go - prove the ownership - signature: - type: object - properties: - type_url: - type: string - description: >- - A URL/resource name that uniquely identifies the type of the - serialized + foo := &pb.Foo{...} + any, err := ptypes.MarshalAny(foo) + ... + foo := &pb.Foo{} + if err := ptypes.UnmarshalAny(any, foo); err != nil { + ... + } - protocol buffer message. This string must contain at least + The pack methods provided by protobuf library will by + default use - one "/" character. The last segment of the URL's path must - represent + 'type.googleapis.com/full.type.name' as the type URL and the + unpack - the fully qualified name of the type (as in + methods only use the fully qualified type name after the + last '/' - `path/google.protobuf.Duration`). The name should be in a - canonical form + in the type URL, for example "foo.bar.com/x/y.z" will yield + type - (e.g., leading "." is not accepted). + name "y.z". - In practice, teams usually precompile into the binary all types - that they - expect it to use in the context of Any. However, for URLs which - use the + JSON - scheme `http`, `https`, or no scheme, one can optionally set up a - type + ==== - server that maps type URLs to message definitions as follows: + The JSON representation of an `Any` value uses the regular + representation of the deserialized, embedded message, with + an - * If no scheme is provided, `https` is assumed. + additional field `@type` which contains the type URL. + Example: - * An HTTP GET on the URL must yield a [google.protobuf.Type][] - value in binary format, or produce an error. - * Applications are allowed to cache lookup results based on the - URL, or have them precompiled into a binary to avoid any - lookup. Therefore, binary compatibility needs to be preserved - on changes to types. (Use versioned type names to manage - breaking changes.) + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } - Note: this functionality is not currently available in the - official + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } - protobuf release, and it is not used for type URLs beginning with + If the embedded message type is well-known and has a custom + JSON - type.googleapis.com. + representation, that representation will be embedded adding + a field + `value` which holds the custom JSON in addition to the + `@type` - Schemes other than `http`, `https` (or the empty scheme) might be + field. Example (for message [google.protobuf.Duration][]): - used with implementation specific semantics. - value: - type: string - format: byte - description: >- - Must be a valid serialized protocol buffer of the above specified - type. - description: >- - `Any` contains an arbitrary serialized protocol buffer message along - with a + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + parameters: + - name: denom + description: coin denom to query the circulating supply for + in: path + required: true + type: string + - name: divider_exponent + description: >- + divider_exponent is a factor used to power the divider used to + convert the - URL that describes the type of the serialized message. + supply to the desired representation. + in: query + required: false + type: string + format: uint64 + tags: + - Query +definitions: + cosmos.base.query.v1beta1.PageRequest: + type: object + properties: + key: + type: string + format: byte + description: |- + key is a value returned in PageResponse.next_key to begin + querying the next page most efficiently. Only one of offset or key + should be set. + offset: + type: string + format: uint64 + description: |- + offset is a numeric offset that can be used when key is unavailable. + It is less efficient than using key. Only one of offset or key should + be set. + limit: + type: string + format: uint64 + description: >- + limit is the total number of results to be returned in the result + page. + If left empty it will default to a value to be set by each app. + count_total: + type: boolean + format: boolean + description: >- + count_total is set to true to indicate that the result set should + include - Protobuf library provides support to pack/unpack Any values in the - form + a count of the total number of items available for pagination in UIs. - of utility functions or additional generated methods of the Any type. + count_total is only respected when offset is used. It is ignored when + key + is set. + reverse: + type: boolean + format: boolean + description: >- + reverse is set to true if results are to be returned in the descending + order. - Example 1: Pack and unpack a message in C++. - Foo foo = ...; - Any any; - any.PackFrom(foo); - ... - if (any.UnpackTo(&foo)) { - ... - } - - Example 2: Pack and unpack a message in Java. - - Foo foo = ...; - Any any = Any.pack(foo); - ... - if (any.is(Foo.class)) { - foo = any.unpack(Foo.class); - } - - Example 3: Pack and unpack a message in Python. - - foo = Foo(...) - any = Any() - any.Pack(foo) - ... - if any.Is(Foo.DESCRIPTOR): - any.Unpack(foo) - ... - - Example 4: Pack and unpack a message in Go - - foo := &pb.Foo{...} - any, err := ptypes.MarshalAny(foo) - ... - foo := &pb.Foo{} - if err := ptypes.UnmarshalAny(any, foo); err != nil { - ... - } - - The pack methods provided by protobuf library will by default use - - 'type.googleapis.com/full.type.name' as the type URL and the unpack - - methods only use the fully qualified type name after the last '/' - - in the type URL, for example "foo.bar.com/x/y.z" will yield type - - name "y.z". - - - - JSON - - ==== - - The JSON representation of an `Any` value uses the regular - - representation of the deserialized, embedded message, with an - - additional field `@type` which contains the type URL. Example: - - package google.profile; - message Person { - string first_name = 1; - string last_name = 2; - } - - { - "@type": "type.googleapis.com/google.profile.Person", - "firstName": , - "lastName": - } - - If the embedded message type is well-known and has a custom JSON - - representation, that representation will be embedded adding a field - - `value` which holds the custom JSON in addition to the `@type` - - field. Example (for message [google.protobuf.Duration][]): + Since: cosmos-sdk 0.43 + description: |- + message SomeRequest { + Foo some_parameter = 1; + PageRequest pagination = 2; + } + title: |- + PageRequest is to be embedded in gRPC request messages for efficient + pagination. Ex: + cosmos.base.query.v1beta1.PageResponse: + type: object + properties: + next_key: + type: string + format: byte + title: |- + next_key is the key to be passed to PageRequest.key to + query the next page most efficiently + total: + type: string + format: uint64 + title: |- + total is total number of results available if PageRequest.count_total + was set, its value is undefined otherwise + description: |- + PageResponse is to be embedded in gRPC response messages where the + corresponding request message has used PageRequest. - { - "@type": "type.googleapis.com/google.protobuf.Duration", - "value": "1.212s" - } - title: Signature represents the hex-encoded signature of the PlainText value - plain_text: + message SomeResponse { + repeated Bar results = 1; + PageResponse page = 2; + } + cosmos.base.v1beta1.Coin: + type: object + properties: + denom: type: string - title: >- - PlainText represents the hex-encoded value signed in order to produce - the + amount: + type: string + description: |- + Coin defines a token with a denomination and an amount. - Signature - title: |- - Proof contains all the data used to verify a signature when linking an - account to a profile - desmos.profiles.v2.QueryApplicationLinkByClientIDResponse: + NOTE: The amount field is an Int which implements the custom method + signatures required by gogoproto. + desmos.profiles.v2.ApplicationLink: type: object properties: - link: + user: + type: string + title: User to which the link is associated + data: + title: Data contains the details of this specific link type: object properties: - user: + application: type: string - title: User to which the link is associated - data: - title: Data contains the details of this specific link + title: 'The application name (eg. Twitter, GitHub, etc)' + username: + type: string + title: 'Username on the application (eg. Twitter tag, GitHub profile, etc)' + state: + title: State of the link + type: string + enum: + - APPLICATION_LINK_STATE_INITIALIZED_UNSPECIFIED + - APPLICATION_LINK_STATE_VERIFICATION_STARTED + - APPLICATION_LINK_STATE_VERIFICATION_ERROR + - APPLICATION_LINK_STATE_VERIFICATION_SUCCESS + - APPLICATION_LINK_STATE_TIMED_OUT + default: APPLICATION_LINK_STATE_INITIALIZED_UNSPECIFIED + description: >- + - APPLICATION_LINK_STATE_INITIALIZED_UNSPECIFIED: A link has just been + initialized + - APPLICATION_LINK_STATE_VERIFICATION_STARTED: A link has just started being verified + - APPLICATION_LINK_STATE_VERIFICATION_ERROR: A link has errored during the verification process + - APPLICATION_LINK_STATE_VERIFICATION_SUCCESS: A link has being verified successfully + - APPLICATION_LINK_STATE_TIMED_OUT: A link has timed out while waiting for the verification + oracle_request: + title: OracleRequest represents the request that has been made to the oracle + type: object + properties: + id: + type: string + format: uint64 + title: ID is the ID of the request + oracle_script_id: + type: string + format: uint64 + title: OracleScriptID is ID of an oracle script + call_data: + title: CallData contains the data used to perform the oracle request type: object properties: application: type: string - title: 'The application name (eg. Twitter, GitHub, etc)' - username: + title: The application for which the ownership should be verified + call_data: type: string title: >- - Username on the application (eg. Twitter tag, GitHub profile, - etc) - state: - title: State of the link + The hex encoded call data that should be used to verify the + application + + account ownership + client_id: type: string - enum: - - APPLICATION_LINK_STATE_INITIALIZED_UNSPECIFIED - - APPLICATION_LINK_STATE_VERIFICATION_STARTED - - APPLICATION_LINK_STATE_VERIFICATION_ERROR - - APPLICATION_LINK_STATE_VERIFICATION_SUCCESS - - APPLICATION_LINK_STATE_TIMED_OUT - default: APPLICATION_LINK_STATE_INITIALIZED_UNSPECIFIED - description: >- - - APPLICATION_LINK_STATE_INITIALIZED_UNSPECIFIED: A link has just - been initialized - - APPLICATION_LINK_STATE_VERIFICATION_STARTED: A link has just started being verified - - APPLICATION_LINK_STATE_VERIFICATION_ERROR: A link has errored during the verification process - - APPLICATION_LINK_STATE_VERIFICATION_SUCCESS: A link has being verified successfully - - APPLICATION_LINK_STATE_TIMED_OUT: A link has timed out while waiting for the verification - oracle_request: title: >- - OracleRequest represents the request that has been made to the - oracle + ClientID represents the ID of the client that has called the + oracle script + result: + title: |- + Data coming from the result of the verification. + Only available when the state is STATE_SUCCESS + type: object + properties: + success: + title: Success represents a successful verification type: object properties: - id: - type: string - format: uint64 - title: ID is the ID of the request - oracle_script_id: + value: type: string - format: uint64 - title: OracleScriptID is ID of an oracle script - call_data: - title: CallData contains the data used to perform the oracle request - type: object - properties: - application: - type: string - title: The application for which the ownership should be verified - call_data: - type: string - title: >- - The hex encoded call data that should be used to verify - the application - - account ownership - client_id: + title: Hex-encoded value that has be signed by the profile + signature: type: string title: >- - ClientID represents the ID of the client that has called the - oracle script - result: - title: |- - Data coming from the result of the verification. - Only available when the state is STATE_SUCCESS + Hex-encoded signature that has been produced by signing the + value + failed: + title: Failed represents a failed verification type: object properties: - success: - title: Success represents a successful verification - type: object - properties: - value: - type: string - title: Hex-encoded value that has be signed by the profile - signature: - type: string - title: >- - Hex-encoded signature that has been produced by signing - the value - failed: - title: Failed represents a failed verification - type: object - properties: - error: - type: string - title: Error that is associated with the failure - creation_time: - type: string - format: date-time - title: CreationTime represents the time in which the link was created - title: >- - ApplicationLink contains the data of a link to a centralized - application + error: + type: string + title: Error that is associated with the failure + creation_time: + type: string + format: date-time + title: CreationTime represents the time in which the link was created + title: ApplicationLink contains the data of a link to a centralized application + desmos.profiles.v2.ApplicationLinkState: + type: string + enum: + - APPLICATION_LINK_STATE_INITIALIZED_UNSPECIFIED + - APPLICATION_LINK_STATE_VERIFICATION_STARTED + - APPLICATION_LINK_STATE_VERIFICATION_ERROR + - APPLICATION_LINK_STATE_VERIFICATION_SUCCESS + - APPLICATION_LINK_STATE_TIMED_OUT + default: APPLICATION_LINK_STATE_INITIALIZED_UNSPECIFIED + description: >- + - APPLICATION_LINK_STATE_INITIALIZED_UNSPECIFIED: A link has just been + initialized + - APPLICATION_LINK_STATE_VERIFICATION_STARTED: A link has just started being verified + - APPLICATION_LINK_STATE_VERIFICATION_ERROR: A link has errored during the verification process + - APPLICATION_LINK_STATE_VERIFICATION_SUCCESS: A link has being verified successfully + - APPLICATION_LINK_STATE_TIMED_OUT: A link has timed out while waiting for the verification title: |- - QueryApplicationLinkByClientIDResponse contains the data returned by the - request allowing to get an application link using a client id - desmos.profiles.v2.QueryApplicationLinkOwnersResponse: + ApplicationLinkState defines if an application link is in the following + states: STARTED, ERRORED, SUCCESSFUL, TIMED_OUT + desmos.profiles.v2.BioParams: type: object properties: - owners: - type: array - items: - type: object - properties: - user: - type: string - application: - type: string - username: - type: string - title: >- - ApplicationLinkOwnerDetails contains the details of a single - application - - link owner - title: Addresses of the application links owners - pagination: - title: Pagination defines the pagination response - type: object - properties: - next_key: - type: string - format: byte - title: |- - next_key is the key to be passed to PageRequest.key to - query the next page most efficiently - total: - type: string - format: uint64 - title: >- - total is total number of results available if - PageRequest.count_total - - was set, its value is undefined otherwise - description: |- - PageResponse is to be embedded in gRPC response messages where the - corresponding request message has used PageRequest. - - message SomeResponse { - repeated Bar results = 1; - PageResponse page = 2; - } - description: >- - QueryApplicationLinkOwnersResponse contains the data returned by the - request - - allowing to get application link owners. - desmos.profiles.v2.QueryApplicationLinkOwnersResponse.ApplicationLinkOwnerDetails: + max_length: + type: string + format: byte + title: BioParams defines the parameters related to profile biography + desmos.profiles.v2.ChainConfig: type: object properties: - user: - type: string - application: - type: string - username: + name: type: string - title: |- - ApplicationLinkOwnerDetails contains the details of a single application - link owner - desmos.profiles.v2.QueryApplicationLinksResponse: + description: ChainConfig contains the data of the chain with which the link is made. + desmos.profiles.v2.ChainLink: type: object properties: - links: - type: array - items: - type: object - properties: - user: - type: string - title: User to which the link is associated - data: - title: Data contains the details of this specific link - type: object - properties: - application: - type: string - title: 'The application name (eg. Twitter, GitHub, etc)' - username: - type: string - title: >- - Username on the application (eg. Twitter tag, GitHub - profile, etc) - state: - title: State of the link - type: string - enum: - - APPLICATION_LINK_STATE_INITIALIZED_UNSPECIFIED - - APPLICATION_LINK_STATE_VERIFICATION_STARTED - - APPLICATION_LINK_STATE_VERIFICATION_ERROR - - APPLICATION_LINK_STATE_VERIFICATION_SUCCESS - - APPLICATION_LINK_STATE_TIMED_OUT - default: APPLICATION_LINK_STATE_INITIALIZED_UNSPECIFIED - description: >- - - APPLICATION_LINK_STATE_INITIALIZED_UNSPECIFIED: A link has - just been initialized - - APPLICATION_LINK_STATE_VERIFICATION_STARTED: A link has just started being verified - - APPLICATION_LINK_STATE_VERIFICATION_ERROR: A link has errored during the verification process - - APPLICATION_LINK_STATE_VERIFICATION_SUCCESS: A link has being verified successfully - - APPLICATION_LINK_STATE_TIMED_OUT: A link has timed out while waiting for the verification - oracle_request: - title: >- - OracleRequest represents the request that has been made to the - oracle - type: object - properties: - id: - type: string - format: uint64 - title: ID is the ID of the request - oracle_script_id: - type: string - format: uint64 - title: OracleScriptID is ID of an oracle script - call_data: - title: >- - CallData contains the data used to perform the oracle - request - type: object - properties: - application: - type: string - title: >- - The application for which the ownership should be - verified - call_data: - type: string - title: >- - The hex encoded call data that should be used to verify - the application - - account ownership - client_id: - type: string - title: >- - ClientID represents the ID of the client that has called the - oracle script - result: - title: |- - Data coming from the result of the verification. - Only available when the state is STATE_SUCCESS - type: object - properties: - success: - title: Success represents a successful verification - type: object - properties: - value: - type: string - title: Hex-encoded value that has be signed by the profile - signature: - type: string - title: >- - Hex-encoded signature that has been produced by signing - the value - failed: - title: Failed represents a failed verification - type: object - properties: - error: - type: string - title: Error that is associated with the failure - creation_time: - type: string - format: date-time - title: CreationTime represents the time in which the link was created - title: >- - ApplicationLink contains the data of a link to a centralized - application - pagination: - title: Pagination defines the pagination response + user: + type: string + title: User defines the destination profile address to link + address: type: object properties: - next_key: - type: string - format: byte - title: |- - next_key is the key to be passed to PageRequest.key to - query the next page most efficiently - total: + type_url: type: string - format: uint64 - title: >- - total is total number of results available if - PageRequest.count_total + description: >- + A URL/resource name that uniquely identifies the type of the + serialized - was set, its value is undefined otherwise - description: |- - PageResponse is to be embedded in gRPC response messages where the - corresponding request message has used PageRequest. + protocol buffer message. This string must contain at least - message SomeResponse { - repeated Bar results = 1; - PageResponse page = 2; - } - title: |- - QueryApplicationLinksResponse represents the response to the query used - to get the application links for a specific user - desmos.profiles.v2.QueryChainLinkOwnersResponse: - type: object - properties: - owners: - type: array - items: - type: object - properties: - user: - type: string - chain_name: - type: string - target: - type: string - title: >- - ChainLinkOwnerDetails contains the details of a single chain link - owner - title: Addresses of the chain links owners - pagination: - title: Pagination defines the pagination response - type: object - properties: - next_key: - type: string - format: byte - title: |- - next_key is the key to be passed to PageRequest.key to - query the next page most efficiently - total: - type: string - format: uint64 - title: >- - total is total number of results available if - PageRequest.count_total + one "/" character. The last segment of the URL's path must + represent - was set, its value is undefined otherwise - description: |- - PageResponse is to be embedded in gRPC response messages where the - corresponding request message has used PageRequest. + the fully qualified name of the type (as in - message SomeResponse { - repeated Bar results = 1; - PageResponse page = 2; - } - description: |- - QueryChainLinkOwnersResponse contains the data returned by the request - allowing to get chain link owners. - desmos.profiles.v2.QueryChainLinkOwnersResponse.ChainLinkOwnerDetails: - type: object - properties: - user: - type: string - chain_name: - type: string - target: - type: string - title: ChainLinkOwnerDetails contains the details of a single chain link owner - desmos.profiles.v2.QueryChainLinksResponse: - type: object - properties: - links: - type: array - items: - type: object - properties: - user: - type: string - title: User defines the destination profile address to link - address: - type: object - properties: - type_url: - type: string - description: >- - A URL/resource name that uniquely identifies the type of the - serialized + `path/google.protobuf.Duration`). The name should be in a + canonical form - protocol buffer message. This string must contain at least + (e.g., leading "." is not accepted). - one "/" character. The last segment of the URL's path must - represent - the fully qualified name of the type (as in + In practice, teams usually precompile into the binary all types + that they - `path/google.protobuf.Duration`). The name should be in a - canonical form + expect it to use in the context of Any. However, for URLs which + use the - (e.g., leading "." is not accepted). + scheme `http`, `https`, or no scheme, one can optionally set up a + type + server that maps type URLs to message definitions as follows: - In practice, teams usually precompile into the binary all - types that they - expect it to use in the context of Any. However, for URLs - which use the + * If no scheme is provided, `https` is assumed. - scheme `http`, `https`, or no scheme, one can optionally set - up a type + * An HTTP GET on the URL must yield a [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based on the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) - server that maps type URLs to message definitions as - follows: + Note: this functionality is not currently available in the + official + protobuf release, and it is not used for type URLs beginning with - * If no scheme is provided, `https` is assumed. + type.googleapis.com. - * An HTTP GET on the URL must yield a - [google.protobuf.Type][] - value in binary format, or produce an error. - * Applications are allowed to cache lookup results based on - the - URL, or have them precompiled into a binary to avoid any - lookup. Therefore, binary compatibility needs to be preserved - on changes to types. (Use versioned type names to manage - breaking changes.) - Note: this functionality is not currently available in the - official + Schemes other than `http`, `https` (or the empty scheme) might be - protobuf release, and it is not used for type URLs beginning - with + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above specified + type. + description: >- + `Any` contains an arbitrary serialized protocol buffer message along + with a - type.googleapis.com. + URL that describes the type of the serialized message. - Schemes other than `http`, `https` (or the empty scheme) - might be + Protobuf library provides support to pack/unpack Any values in the + form - used with implementation specific semantics. - value: - type: string - format: byte - description: >- - Must be a valid serialized protocol buffer of the above - specified type. - description: >- - `Any` contains an arbitrary serialized protocol buffer message - along with a + of utility functions or additional generated methods of the Any type. - URL that describes the type of the serialized message. + Example 1: Pack and unpack a message in C++. - Protobuf library provides support to pack/unpack Any values in - the form + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } - of utility functions or additional generated methods of the Any - type. + Example 2: Pack and unpack a message in Java. + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } - Example 1: Pack and unpack a message in C++. + Example 3: Pack and unpack a message in Python. - Foo foo = ...; - Any any; - any.PackFrom(foo); - ... - if (any.UnpackTo(&foo)) { - ... - } + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... - Example 2: Pack and unpack a message in Java. + Example 4: Pack and unpack a message in Go - Foo foo = ...; - Any any = Any.pack(foo); - ... - if (any.is(Foo.class)) { - foo = any.unpack(Foo.class); - } + foo := &pb.Foo{...} + any, err := ptypes.MarshalAny(foo) + ... + foo := &pb.Foo{} + if err := ptypes.UnmarshalAny(any, foo); err != nil { + ... + } - Example 3: Pack and unpack a message in Python. + The pack methods provided by protobuf library will by default use - foo = Foo(...) - any = Any() - any.Pack(foo) - ... - if any.Is(Foo.DESCRIPTOR): - any.Unpack(foo) - ... + 'type.googleapis.com/full.type.name' as the type URL and the unpack - Example 4: Pack and unpack a message in Go + methods only use the fully qualified type name after the last '/' - foo := &pb.Foo{...} - any, err := ptypes.MarshalAny(foo) - ... - foo := &pb.Foo{} - if err := ptypes.UnmarshalAny(any, foo); err != nil { - ... - } + in the type URL, for example "foo.bar.com/x/y.z" will yield type - The pack methods provided by protobuf library will by default - use + name "y.z". - 'type.googleapis.com/full.type.name' as the type URL and the - unpack - methods only use the fully qualified type name after the last - '/' - in the type URL, for example "foo.bar.com/x/y.z" will yield type + JSON - name "y.z". + ==== + The JSON representation of an `Any` value uses the regular + representation of the deserialized, embedded message, with an - JSON + additional field `@type` which contains the type URL. Example: - ==== + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } - The JSON representation of an `Any` value uses the regular + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } - representation of the deserialized, embedded message, with an + If the embedded message type is well-known and has a custom JSON - additional field `@type` which contains the type URL. Example: + representation, that representation will be embedded adding a field - package google.profile; - message Person { - string first_name = 1; - string last_name = 2; - } + `value` which holds the custom JSON in addition to the `@type` - { - "@type": "type.googleapis.com/google.profile.Person", - "firstName": , - "lastName": - } + field. Example (for message [google.protobuf.Duration][]): - If the embedded message type is well-known and has a custom JSON + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + title: >- + Address contains the data of the external chain address to be + connected - representation, that representation will be embedded adding a - field + with the Desmos profile + proof: + title: Proof contains the ownership proof of the external chain address + type: object + properties: + pub_key: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type of the + serialized - `value` which holds the custom JSON in addition to the `@type` + protocol buffer message. This string must contain at least - field. Example (for message [google.protobuf.Duration][]): + one "/" character. The last segment of the URL's path must + represent - { - "@type": "type.googleapis.com/google.protobuf.Duration", - "value": "1.212s" - } - title: >- - Address contains the data of the external chain address to be - connected + the fully qualified name of the type (as in - with the Desmos profile - proof: - title: Proof contains the ownership proof of the external chain address - type: object - properties: - pub_key: - type: object - properties: - type_url: - type: string - description: >- - A URL/resource name that uniquely identifies the type of - the serialized + `path/google.protobuf.Duration`). The name should be in a + canonical form - protocol buffer message. This string must contain at - least + (e.g., leading "." is not accepted). - one "/" character. The last segment of the URL's path - must represent - the fully qualified name of the type (as in + In practice, teams usually precompile into the binary all + types that they - `path/google.protobuf.Duration`). The name should be in - a canonical form + expect it to use in the context of Any. However, for URLs + which use the - (e.g., leading "." is not accepted). + scheme `http`, `https`, or no scheme, one can optionally set + up a type + server that maps type URLs to message definitions as follows: - In practice, teams usually precompile into the binary - all types that they - expect it to use in the context of Any. However, for - URLs which use the + * If no scheme is provided, `https` is assumed. - scheme `http`, `https`, or no scheme, one can optionally - set up a type + * An HTTP GET on the URL must yield a [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based on + the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) - server that maps type URLs to message definitions as - follows: + Note: this functionality is not currently available in the + official + protobuf release, and it is not used for type URLs beginning + with - * If no scheme is provided, `https` is assumed. + type.googleapis.com. - * An HTTP GET on the URL must yield a - [google.protobuf.Type][] - value in binary format, or produce an error. - * Applications are allowed to cache lookup results based - on the - URL, or have them precompiled into a binary to avoid any - lookup. Therefore, binary compatibility needs to be preserved - on changes to types. (Use versioned type names to manage - breaking changes.) - Note: this functionality is not currently available in - the official + Schemes other than `http`, `https` (or the empty scheme) might + be - protobuf release, and it is not used for type URLs - beginning with + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above + specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer message + along with a - type.googleapis.com. + URL that describes the type of the serialized message. - Schemes other than `http`, `https` (or the empty scheme) - might be + Protobuf library provides support to pack/unpack Any values in the + form - used with implementation specific semantics. - value: - type: string - format: byte - description: >- - Must be a valid serialized protocol buffer of the above - specified type. - description: >- - `Any` contains an arbitrary serialized protocol buffer - message along with a + of utility functions or additional generated methods of the Any + type. - URL that describes the type of the serialized message. + Example 1: Pack and unpack a message in C++. - Protobuf library provides support to pack/unpack Any values - in the form + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } - of utility functions or additional generated methods of the - Any type. + Example 2: Pack and unpack a message in Java. + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } - Example 1: Pack and unpack a message in C++. + Example 3: Pack and unpack a message in Python. - Foo foo = ...; - Any any; - any.PackFrom(foo); - ... - if (any.UnpackTo(&foo)) { - ... - } + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... - Example 2: Pack and unpack a message in Java. + Example 4: Pack and unpack a message in Go - Foo foo = ...; - Any any = Any.pack(foo); - ... - if (any.is(Foo.class)) { - foo = any.unpack(Foo.class); - } + foo := &pb.Foo{...} + any, err := ptypes.MarshalAny(foo) + ... + foo := &pb.Foo{} + if err := ptypes.UnmarshalAny(any, foo); err != nil { + ... + } - Example 3: Pack and unpack a message in Python. + The pack methods provided by protobuf library will by default use - foo = Foo(...) - any = Any() - any.Pack(foo) - ... - if any.Is(Foo.DESCRIPTOR): - any.Unpack(foo) - ... + 'type.googleapis.com/full.type.name' as the type URL and the + unpack - Example 4: Pack and unpack a message in Go + methods only use the fully qualified type name after the last '/' - foo := &pb.Foo{...} - any, err := ptypes.MarshalAny(foo) - ... - foo := &pb.Foo{} - if err := ptypes.UnmarshalAny(any, foo); err != nil { - ... - } + in the type URL, for example "foo.bar.com/x/y.z" will yield type - The pack methods provided by protobuf library will by - default use + name "y.z". - 'type.googleapis.com/full.type.name' as the type URL and the - unpack - methods only use the fully qualified type name after the - last '/' - in the type URL, for example "foo.bar.com/x/y.z" will yield - type + JSON - name "y.z". + ==== + The JSON representation of an `Any` value uses the regular + representation of the deserialized, embedded message, with an - JSON + additional field `@type` which contains the type URL. Example: - ==== + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } - The JSON representation of an `Any` value uses the regular + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } - representation of the deserialized, embedded message, with - an + If the embedded message type is well-known and has a custom JSON - additional field `@type` which contains the type URL. - Example: + representation, that representation will be embedded adding a + field - package google.profile; - message Person { - string first_name = 1; - string last_name = 2; - } + `value` which holds the custom JSON in addition to the `@type` - { - "@type": "type.googleapis.com/google.profile.Person", - "firstName": , - "lastName": - } + field. Example (for message [google.protobuf.Duration][]): - If the embedded message type is well-known and has a custom - JSON + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + title: >- + PubKey represents the public key associated with the address for + which to - representation, that representation will be embedded adding - a field + prove the ownership + signature: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type of the + serialized - `value` which holds the custom JSON in addition to the - `@type` + protocol buffer message. This string must contain at least - field. Example (for message [google.protobuf.Duration][]): + one "/" character. The last segment of the URL's path must + represent - { - "@type": "type.googleapis.com/google.protobuf.Duration", - "value": "1.212s" - } - title: >- - PubKey represents the public key associated with the address - for which to + the fully qualified name of the type (as in - prove the ownership - signature: - type: object - properties: - type_url: - type: string - description: >- - A URL/resource name that uniquely identifies the type of - the serialized + `path/google.protobuf.Duration`). The name should be in a + canonical form - protocol buffer message. This string must contain at - least + (e.g., leading "." is not accepted). - one "/" character. The last segment of the URL's path - must represent - the fully qualified name of the type (as in + In practice, teams usually precompile into the binary all + types that they - `path/google.protobuf.Duration`). The name should be in - a canonical form + expect it to use in the context of Any. However, for URLs + which use the - (e.g., leading "." is not accepted). + scheme `http`, `https`, or no scheme, one can optionally set + up a type + server that maps type URLs to message definitions as follows: - In practice, teams usually precompile into the binary - all types that they - expect it to use in the context of Any. However, for - URLs which use the + * If no scheme is provided, `https` is assumed. - scheme `http`, `https`, or no scheme, one can optionally - set up a type + * An HTTP GET on the URL must yield a [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based on + the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) - server that maps type URLs to message definitions as - follows: + Note: this functionality is not currently available in the + official + protobuf release, and it is not used for type URLs beginning + with - * If no scheme is provided, `https` is assumed. + type.googleapis.com. - * An HTTP GET on the URL must yield a - [google.protobuf.Type][] - value in binary format, or produce an error. - * Applications are allowed to cache lookup results based - on the - URL, or have them precompiled into a binary to avoid any - lookup. Therefore, binary compatibility needs to be preserved - on changes to types. (Use versioned type names to manage - breaking changes.) - Note: this functionality is not currently available in - the official + Schemes other than `http`, `https` (or the empty scheme) might + be - protobuf release, and it is not used for type URLs - beginning with + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above + specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer message + along with a - type.googleapis.com. + URL that describes the type of the serialized message. - Schemes other than `http`, `https` (or the empty scheme) - might be + Protobuf library provides support to pack/unpack Any values in the + form - used with implementation specific semantics. - value: - type: string - format: byte - description: >- - Must be a valid serialized protocol buffer of the above - specified type. - description: >- - `Any` contains an arbitrary serialized protocol buffer - message along with a + of utility functions or additional generated methods of the Any + type. - URL that describes the type of the serialized message. + Example 1: Pack and unpack a message in C++. - Protobuf library provides support to pack/unpack Any values - in the form + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } - of utility functions or additional generated methods of the - Any type. + Example 2: Pack and unpack a message in Java. + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } - Example 1: Pack and unpack a message in C++. + Example 3: Pack and unpack a message in Python. - Foo foo = ...; - Any any; - any.PackFrom(foo); - ... - if (any.UnpackTo(&foo)) { - ... - } + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... - Example 2: Pack and unpack a message in Java. + Example 4: Pack and unpack a message in Go - Foo foo = ...; - Any any = Any.pack(foo); - ... - if (any.is(Foo.class)) { - foo = any.unpack(Foo.class); - } + foo := &pb.Foo{...} + any, err := ptypes.MarshalAny(foo) + ... + foo := &pb.Foo{} + if err := ptypes.UnmarshalAny(any, foo); err != nil { + ... + } - Example 3: Pack and unpack a message in Python. + The pack methods provided by protobuf library will by default use - foo = Foo(...) - any = Any() - any.Pack(foo) - ... - if any.Is(Foo.DESCRIPTOR): - any.Unpack(foo) - ... + 'type.googleapis.com/full.type.name' as the type URL and the + unpack - Example 4: Pack and unpack a message in Go + methods only use the fully qualified type name after the last '/' - foo := &pb.Foo{...} - any, err := ptypes.MarshalAny(foo) - ... - foo := &pb.Foo{} - if err := ptypes.UnmarshalAny(any, foo); err != nil { - ... - } + in the type URL, for example "foo.bar.com/x/y.z" will yield type - The pack methods provided by protobuf library will by - default use + name "y.z". - 'type.googleapis.com/full.type.name' as the type URL and the - unpack - methods only use the fully qualified type name after the - last '/' - in the type URL, for example "foo.bar.com/x/y.z" will yield - type + JSON - name "y.z". + ==== + The JSON representation of an `Any` value uses the regular + representation of the deserialized, embedded message, with an - JSON + additional field `@type` which contains the type URL. Example: - ==== + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } - The JSON representation of an `Any` value uses the regular + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } - representation of the deserialized, embedded message, with - an + If the embedded message type is well-known and has a custom JSON - additional field `@type` which contains the type URL. - Example: + representation, that representation will be embedded adding a + field - package google.profile; - message Person { - string first_name = 1; - string last_name = 2; - } + `value` which holds the custom JSON in addition to the `@type` - { - "@type": "type.googleapis.com/google.profile.Person", - "firstName": , - "lastName": - } + field. Example (for message [google.protobuf.Duration][]): - If the embedded message type is well-known and has a custom - JSON - - representation, that representation will be embedded adding - a field - - `value` which holds the custom JSON in addition to the - `@type` - - field. Example (for message [google.protobuf.Duration][]): - - { - "@type": "type.googleapis.com/google.protobuf.Duration", - "value": "1.212s" - } - title: >- - Signature represents the hex-encoded signature of the - PlainText value - plain_text: - type: string - title: >- - PlainText represents the hex-encoded value signed in order - to produce the - - Signature - chain_config: - title: ChainConfig contains the configuration of the external chain - type: object - properties: - name: - type: string - description: >- - ChainConfig contains the data of the chain with which the link - is made. - creation_time: - type: string - format: date-time - title: >- - CreationTime represents the time in which the link has been - created - title: >- - ChainLink contains the data representing either an inter- or cross- - chain + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + title: >- + Signature represents the hex-encoded signature of the PlainText + value + plain_text: + type: string + title: >- + PlainText represents the hex-encoded value signed in order to + produce the - link - pagination: - title: Pagination defines the pagination response + Signature + chain_config: + title: ChainConfig contains the configuration of the external chain type: object properties: - next_key: - type: string - format: byte - title: |- - next_key is the key to be passed to PageRequest.key to - query the next page most efficiently - total: + name: type: string - format: uint64 - title: >- - total is total number of results available if - PageRequest.count_total + description: >- + ChainConfig contains the data of the chain with which the link is + made. + creation_time: + type: string + format: date-time + title: CreationTime represents the time in which the link has been created + title: |- + ChainLink contains the data representing either an inter- or cross- chain + link + desmos.profiles.v2.DTagParams: + type: object + properties: + reg_ex: + type: string + min_length: + type: string + format: byte + max_length: + type: string + format: byte + title: DTagParams defines the parameters related to profile DTags + desmos.profiles.v2.DTagTransferRequest: + type: object + properties: + dtag_to_trade: + type: string + title: >- + DTagToTrade contains the value of the DTag that should be transferred + from - was set, its value is undefined otherwise - description: |- - PageResponse is to be embedded in gRPC response messages where the - corresponding request message has used PageRequest. + the receiver of the request to the sender + sender: + type: string + title: Sender represents the address of the account that sent the request + receiver: + type: string + title: >- + Receiver represents the receiver of the request that, if accepted, + will - message SomeResponse { - repeated Bar results = 1; - PageResponse page = 2; - } - description: |- - QueryChainLinksResponse is the response type for the - Query/ChainLinks RPC method. - desmos.profiles.v2.QueryIncomingDTagTransferRequestsResponse: + give to the sender their DTag + title: DTagTransferRequest represent a DTag transfer request between two users + desmos.profiles.v2.Data: type: object properties: - requests: + application: + type: string + title: 'The application name (eg. Twitter, GitHub, etc)' + username: + type: string + title: 'Username on the application (eg. Twitter tag, GitHub profile, etc)' + title: |- + Data contains the data associated to a specific user of a + generic centralized application + desmos.profiles.v2.NicknameParams: + type: object + properties: + min_length: + type: string + format: byte + max_length: + type: string + format: byte + title: NicknameParams defines the parameters related to the profiles nicknames + desmos.profiles.v2.OracleParams: + type: object + properties: + script_id: + type: string + format: uint64 + title: >- + ScriptID represents the ID of the oracle script to be called to verify + the + + data + ask_count: + type: string + format: uint64 + title: >- + AskCount represents the number of oracles to which ask to verify the + data + min_count: + type: string + format: uint64 + title: >- + MinCount represents the minimum count of oracles that should complete + the + + verification successfully + prepare_gas: + type: string + format: uint64 + title: >- + PrepareGas represents the amount of gas to be used during the + preparation + + stage of the oracle script + execute_gas: + type: string + format: uint64 + title: >- + ExecuteGas represents the amount of gas to be used during the + execution of + + the oracle script + fee_amount: type: array items: type: object properties: - dtag_to_trade: - type: string - title: >- - DTagToTrade contains the value of the DTag that should be - transferred from - - the receiver of the request to the sender - sender: + denom: type: string - title: >- - Sender represents the address of the account that sent the - request - receiver: + amount: type: string - title: >- - Receiver represents the receiver of the request that, if - accepted, will + description: |- + Coin defines a token with a denomination and an amount. - give to the sender their DTag - title: >- - DTagTransferRequest represent a DTag transfer request between two - users + NOTE: The amount field is an Int which implements the custom method + signatures required by gogoproto. title: >- - Requests represent the list of all the DTag transfer requests made - towards + FeeAmount represents the amount of fees to be payed in order to + execute the - the user - pagination: - title: Pagination defines the pagination response + oracle script + title: |- + OracleParams defines the parameters related to the oracle + that will be used to verify the ownership of a centralized + application account by a Desmos profile + desmos.profiles.v2.OracleRequest: + type: object + properties: + id: + type: string + format: uint64 + title: ID is the ID of the request + oracle_script_id: + type: string + format: uint64 + title: OracleScriptID is ID of an oracle script + call_data: + title: CallData contains the data used to perform the oracle request type: object properties: - next_key: + application: type: string - format: byte - title: |- - next_key is the key to be passed to PageRequest.key to - query the next page most efficiently - total: + title: The application for which the ownership should be verified + call_data: type: string - format: uint64 title: >- - total is total number of results available if - PageRequest.count_total - - was set, its value is undefined otherwise - description: |- - PageResponse is to be embedded in gRPC response messages where the - corresponding request message has used PageRequest. + The hex encoded call data that should be used to verify the + application - message SomeResponse { - repeated Bar results = 1; - PageResponse page = 2; - } - description: |- - QueryIncomingDTagTransferRequestsResponse is the response type for the - Query/IncomingDTagTransferRequests RPC method. - desmos.profiles.v2.QueryParamsResponse: + account ownership + client_id: + type: string + title: >- + ClientID represents the ID of the client that has called the oracle + script + title: |- + OracleRequest represents a generic oracle request used to + verify the ownership of a centralized application account + desmos.profiles.v2.OracleRequest.CallData: type: object properties: - params: + application: + type: string + title: The application for which the ownership should be verified + call_data: + type: string + title: >- + The hex encoded call data that should be used to verify the + application + + account ownership + title: |- + CallData contains the data sent to a single oracle request in order to + verify the ownership of a centralized application by a Desmos profile + desmos.profiles.v2.Params: + type: object + properties: + nickname: type: object properties: - nickname: - type: object - properties: - min_length: - type: string - format: byte - max_length: - type: string - format: byte + min_length: + type: string + format: byte + max_length: + type: string + format: byte + title: >- + NicknameParams defines the parameters related to the profiles + nicknames + dtag: + type: object + properties: + reg_ex: + type: string + min_length: + type: string + format: byte + max_length: + type: string + format: byte + title: DTagParams defines the parameters related to profile DTags + bio: + type: object + properties: + max_length: + type: string + format: byte + title: BioParams defines the parameters related to profile biography + oracle: + type: object + properties: + script_id: + type: string + format: uint64 title: >- - NicknameParams defines the parameters related to the profiles - nicknames - dtag: - type: object - properties: - reg_ex: - type: string - min_length: - type: string - format: byte - max_length: - type: string - format: byte - title: DTagParams defines the parameters related to profile DTags - bio: - type: object - properties: - max_length: - type: string - format: byte - title: BioParams defines the parameters related to profile biography - oracle: - type: object - properties: - script_id: - type: string - format: uint64 - title: >- - ScriptID represents the ID of the oracle script to be called - to verify the + ScriptID represents the ID of the oracle script to be called to + verify the - data - ask_count: - type: string - format: uint64 - title: >- - AskCount represents the number of oracles to which ask to - verify the data - min_count: - type: string - format: uint64 - title: >- - MinCount represents the minimum count of oracles that should - complete the + data + ask_count: + type: string + format: uint64 + title: >- + AskCount represents the number of oracles to which ask to verify + the data + min_count: + type: string + format: uint64 + title: >- + MinCount represents the minimum count of oracles that should + complete the - verification successfully - prepare_gas: - type: string - format: uint64 - title: >- - PrepareGas represents the amount of gas to be used during the - preparation + verification successfully + prepare_gas: + type: string + format: uint64 + title: >- + PrepareGas represents the amount of gas to be used during the + preparation - stage of the oracle script - execute_gas: - type: string - format: uint64 - title: >- - ExecuteGas represents the amount of gas to be used during the - execution of + stage of the oracle script + execute_gas: + type: string + format: uint64 + title: >- + ExecuteGas represents the amount of gas to be used during the + execution of - the oracle script - fee_amount: - type: array - items: - type: object - properties: - denom: - type: string - amount: - type: string - description: >- - Coin defines a token with a denomination and an amount. + the oracle script + fee_amount: + type: array + items: + type: object + properties: + denom: + type: string + amount: + type: string + description: >- + Coin defines a token with a denomination and an amount. - NOTE: The amount field is an Int which implements the custom - method + NOTE: The amount field is an Int which implements the custom + method - signatures required by gogoproto. - title: >- - FeeAmount represents the amount of fees to be payed in order - to execute the + signatures required by gogoproto. + title: >- + FeeAmount represents the amount of fees to be payed in order to + execute the - oracle script - title: |- - OracleParams defines the parameters related to the oracle - that will be used to verify the ownership of a centralized - application account by a Desmos profile - title: Params contains the parameters for the profiles module - description: QueryParamsResponse is the response type for the Query/Params RPC method. - desmos.profiles.v2.QueryProfileResponse: + oracle script + title: |- + OracleParams defines the parameters related to the oracle + that will be used to verify the ownership of a centralized + application account by a Desmos profile + title: Params contains the parameters for the profiles module + desmos.profiles.v2.Proof: type: object properties: - profile: + pub_key: type: object properties: type_url: @@ -10555,413 +10205,309 @@ definitions: "@type": "type.googleapis.com/google.protobuf.Duration", "value": "1.212s" } - description: >- - QueryProfileResponse is the response type for the Query/Profile RPC - method. - desmos.profiles.v2.Result: - type: object - properties: - success: - title: Success represents a successful verification - type: object - properties: - value: - type: string - title: Hex-encoded value that has be signed by the profile - signature: - type: string - title: Hex-encoded signature that has been produced by signing the value - failed: - title: Failed represents a failed verification + title: >- + PubKey represents the public key associated with the address for which + to + + prove the ownership + signature: type: object properties: - error: + type_url: type: string - title: Error that is associated with the failure - title: Result represents a verification result - desmos.profiles.v2.Result.Failed: - type: object - properties: - error: - type: string - title: Error that is associated with the failure - title: |- - Failed is the result of an application link that has not been verified - successfully - desmos.profiles.v2.Result.Success: - type: object - properties: - value: - type: string - title: Hex-encoded value that has be signed by the profile - signature: - type: string - title: Hex-encoded signature that has been produced by signing the value - title: |- - Success is the result of an application link that has been successfully - verified - google.protobuf.Any: - type: object - properties: - type_url: - type: string - description: >- - A URL/resource name that uniquely identifies the type of the - serialized + description: >- + A URL/resource name that uniquely identifies the type of the + serialized - protocol buffer message. This string must contain at least + protocol buffer message. This string must contain at least - one "/" character. The last segment of the URL's path must represent + one "/" character. The last segment of the URL's path must + represent - the fully qualified name of the type (as in + the fully qualified name of the type (as in - `path/google.protobuf.Duration`). The name should be in a canonical - form + `path/google.protobuf.Duration`). The name should be in a + canonical form - (e.g., leading "." is not accepted). + (e.g., leading "." is not accepted). - In practice, teams usually precompile into the binary all types that - they + In practice, teams usually precompile into the binary all types + that they - expect it to use in the context of Any. However, for URLs which use - the + expect it to use in the context of Any. However, for URLs which + use the - scheme `http`, `https`, or no scheme, one can optionally set up a type + scheme `http`, `https`, or no scheme, one can optionally set up a + type - server that maps type URLs to message definitions as follows: + server that maps type URLs to message definitions as follows: - * If no scheme is provided, `https` is assumed. + * If no scheme is provided, `https` is assumed. - * An HTTP GET on the URL must yield a [google.protobuf.Type][] - value in binary format, or produce an error. - * Applications are allowed to cache lookup results based on the - URL, or have them precompiled into a binary to avoid any - lookup. Therefore, binary compatibility needs to be preserved - on changes to types. (Use versioned type names to manage - breaking changes.) + * An HTTP GET on the URL must yield a [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based on the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) - Note: this functionality is not currently available in the official + Note: this functionality is not currently available in the + official - protobuf release, and it is not used for type URLs beginning with + protobuf release, and it is not used for type URLs beginning with - type.googleapis.com. + type.googleapis.com. - Schemes other than `http`, `https` (or the empty scheme) might be + Schemes other than `http`, `https` (or the empty scheme) might be - used with implementation specific semantics. - value: - type: string - format: byte + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above specified + type. description: >- - Must be a valid serialized protocol buffer of the above specified - type. - description: >- - `Any` contains an arbitrary serialized protocol buffer message along with - a + `Any` contains an arbitrary serialized protocol buffer message along + with a - URL that describes the type of the serialized message. + URL that describes the type of the serialized message. - Protobuf library provides support to pack/unpack Any values in the form + Protobuf library provides support to pack/unpack Any values in the + form - of utility functions or additional generated methods of the Any type. + of utility functions or additional generated methods of the Any type. - Example 1: Pack and unpack a message in C++. + Example 1: Pack and unpack a message in C++. - Foo foo = ...; - Any any; - any.PackFrom(foo); - ... - if (any.UnpackTo(&foo)) { - ... - } + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } - Example 2: Pack and unpack a message in Java. + Example 2: Pack and unpack a message in Java. - Foo foo = ...; - Any any = Any.pack(foo); - ... - if (any.is(Foo.class)) { - foo = any.unpack(Foo.class); - } + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } - Example 3: Pack and unpack a message in Python. + Example 3: Pack and unpack a message in Python. - foo = Foo(...) - any = Any() - any.Pack(foo) - ... - if any.Is(Foo.DESCRIPTOR): - any.Unpack(foo) - ... + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... - Example 4: Pack and unpack a message in Go + Example 4: Pack and unpack a message in Go - foo := &pb.Foo{...} - any, err := ptypes.MarshalAny(foo) - ... - foo := &pb.Foo{} - if err := ptypes.UnmarshalAny(any, foo); err != nil { - ... - } + foo := &pb.Foo{...} + any, err := ptypes.MarshalAny(foo) + ... + foo := &pb.Foo{} + if err := ptypes.UnmarshalAny(any, foo); err != nil { + ... + } - The pack methods provided by protobuf library will by default use + The pack methods provided by protobuf library will by default use - 'type.googleapis.com/full.type.name' as the type URL and the unpack + 'type.googleapis.com/full.type.name' as the type URL and the unpack - methods only use the fully qualified type name after the last '/' + methods only use the fully qualified type name after the last '/' - in the type URL, for example "foo.bar.com/x/y.z" will yield type + in the type URL, for example "foo.bar.com/x/y.z" will yield type - name "y.z". + name "y.z". - JSON + JSON - ==== + ==== - The JSON representation of an `Any` value uses the regular + The JSON representation of an `Any` value uses the regular - representation of the deserialized, embedded message, with an + representation of the deserialized, embedded message, with an - additional field `@type` which contains the type URL. Example: + additional field `@type` which contains the type URL. Example: - package google.profile; - message Person { - string first_name = 1; - string last_name = 2; - } + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } - { - "@type": "type.googleapis.com/google.profile.Person", - "firstName": , - "lastName": - } + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } - If the embedded message type is well-known and has a custom JSON + If the embedded message type is well-known and has a custom JSON - representation, that representation will be embedded adding a field + representation, that representation will be embedded adding a field - `value` which holds the custom JSON in addition to the `@type` + `value` which holds the custom JSON in addition to the `@type` - field. Example (for message [google.protobuf.Duration][]): + field. Example (for message [google.protobuf.Duration][]): - { - "@type": "type.googleapis.com/google.protobuf.Duration", - "value": "1.212s" - } - grpc.gateway.runtime.Error: - type: object - properties: - error: - type: string - code: - type: integer - format: int32 - message: + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + title: Signature represents the hex-encoded signature of the PlainText value + plain_text: type: string - details: - type: array - items: - type: object - properties: - type_url: - type: string - description: >- - A URL/resource name that uniquely identifies the type of the - serialized - - protocol buffer message. This string must contain at least - - one "/" character. The last segment of the URL's path must - represent - - the fully qualified name of the type (as in - - `path/google.protobuf.Duration`). The name should be in a - canonical form - - (e.g., leading "." is not accepted). - - - In practice, teams usually precompile into the binary all types - that they + title: >- + PlainText represents the hex-encoded value signed in order to produce + the - expect it to use in the context of Any. However, for URLs which - use the - - scheme `http`, `https`, or no scheme, one can optionally set up - a type - - server that maps type URLs to message definitions as follows: - - - * If no scheme is provided, `https` is assumed. - - * An HTTP GET on the URL must yield a [google.protobuf.Type][] - value in binary format, or produce an error. - * Applications are allowed to cache lookup results based on the - URL, or have them precompiled into a binary to avoid any - lookup. Therefore, binary compatibility needs to be preserved - on changes to types. (Use versioned type names to manage - breaking changes.) - - Note: this functionality is not currently available in the - official - - protobuf release, and it is not used for type URLs beginning - with - - type.googleapis.com. - - - Schemes other than `http`, `https` (or the empty scheme) might - be - - used with implementation specific semantics. - value: - type: string - format: byte - description: >- - Must be a valid serialized protocol buffer of the above - specified type. - description: >- - `Any` contains an arbitrary serialized protocol buffer message along - with a - - URL that describes the type of the serialized message. - - - Protobuf library provides support to pack/unpack Any values in the - form - - of utility functions or additional generated methods of the Any - type. - - - Example 1: Pack and unpack a message in C++. - - Foo foo = ...; - Any any; - any.PackFrom(foo); - ... - if (any.UnpackTo(&foo)) { - ... - } - - Example 2: Pack and unpack a message in Java. - - Foo foo = ...; - Any any = Any.pack(foo); - ... - if (any.is(Foo.class)) { - foo = any.unpack(Foo.class); - } - - Example 3: Pack and unpack a message in Python. - - foo = Foo(...) - any = Any() - any.Pack(foo) - ... - if any.Is(Foo.DESCRIPTOR): - any.Unpack(foo) - ... - - Example 4: Pack and unpack a message in Go - - foo := &pb.Foo{...} - any, err := ptypes.MarshalAny(foo) - ... - foo := &pb.Foo{} - if err := ptypes.UnmarshalAny(any, foo); err != nil { - ... - } - - The pack methods provided by protobuf library will by default use - - 'type.googleapis.com/full.type.name' as the type URL and the unpack - - methods only use the fully qualified type name after the last '/' - - in the type URL, for example "foo.bar.com/x/y.z" will yield type - - name "y.z". - - - - JSON - - ==== - - The JSON representation of an `Any` value uses the regular - - representation of the deserialized, embedded message, with an - - additional field `@type` which contains the type URL. Example: - - package google.profile; - message Person { - string first_name = 1; - string last_name = 2; - } - - { - "@type": "type.googleapis.com/google.profile.Person", - "firstName": , - "lastName": - } - - If the embedded message type is well-known and has a custom JSON - - representation, that representation will be embedded adding a field - - `value` which holds the custom JSON in addition to the `@type` - - field. Example (for message [google.protobuf.Duration][]): + Signature + title: |- + Proof contains all the data used to verify a signature when linking an + account to a profile + desmos.profiles.v2.QueryApplicationLinkByClientIDResponse: + type: object + properties: + link: + type: object + properties: + user: + type: string + title: User to which the link is associated + data: + title: Data contains the details of this specific link + type: object + properties: + application: + type: string + title: 'The application name (eg. Twitter, GitHub, etc)' + username: + type: string + title: >- + Username on the application (eg. Twitter tag, GitHub profile, + etc) + state: + title: State of the link + type: string + enum: + - APPLICATION_LINK_STATE_INITIALIZED_UNSPECIFIED + - APPLICATION_LINK_STATE_VERIFICATION_STARTED + - APPLICATION_LINK_STATE_VERIFICATION_ERROR + - APPLICATION_LINK_STATE_VERIFICATION_SUCCESS + - APPLICATION_LINK_STATE_TIMED_OUT + default: APPLICATION_LINK_STATE_INITIALIZED_UNSPECIFIED + description: >- + - APPLICATION_LINK_STATE_INITIALIZED_UNSPECIFIED: A link has just + been initialized + - APPLICATION_LINK_STATE_VERIFICATION_STARTED: A link has just started being verified + - APPLICATION_LINK_STATE_VERIFICATION_ERROR: A link has errored during the verification process + - APPLICATION_LINK_STATE_VERIFICATION_SUCCESS: A link has being verified successfully + - APPLICATION_LINK_STATE_TIMED_OUT: A link has timed out while waiting for the verification + oracle_request: + title: >- + OracleRequest represents the request that has been made to the + oracle + type: object + properties: + id: + type: string + format: uint64 + title: ID is the ID of the request + oracle_script_id: + type: string + format: uint64 + title: OracleScriptID is ID of an oracle script + call_data: + title: CallData contains the data used to perform the oracle request + type: object + properties: + application: + type: string + title: The application for which the ownership should be verified + call_data: + type: string + title: >- + The hex encoded call data that should be used to verify + the application - { - "@type": "type.googleapis.com/google.protobuf.Duration", - "value": "1.212s" - } - desmos.relationships.v1.QueryBlocksResponse: + account ownership + client_id: + type: string + title: >- + ClientID represents the ID of the client that has called the + oracle script + result: + title: |- + Data coming from the result of the verification. + Only available when the state is STATE_SUCCESS + type: object + properties: + success: + title: Success represents a successful verification + type: object + properties: + value: + type: string + title: Hex-encoded value that has be signed by the profile + signature: + type: string + title: >- + Hex-encoded signature that has been produced by signing + the value + failed: + title: Failed represents a failed verification + type: object + properties: + error: + type: string + title: Error that is associated with the failure + creation_time: + type: string + format: date-time + title: CreationTime represents the time in which the link was created + title: >- + ApplicationLink contains the data of a link to a centralized + application + title: |- + QueryApplicationLinkByClientIDResponse contains the data returned by the + request allowing to get an application link using a client id + desmos.profiles.v2.QueryApplicationLinkOwnersResponse: type: object properties: - blocks: + owners: type: array items: type: object properties: - blocker: + user: type: string - title: Blocker represents the address of the user blocking another one - blocked: + application: type: string - title: Blocked represents the address of the blocked user - reason: - type: string - description: >- - Reason represents the optional reason the user has been blocked - for. - subspace_id: + username: type: string - format: uint64 - title: >- - SubspaceID represents the ID of the subspace inside which the - user should - - be blocked - description: >- - UserBlock represents the fact that the Blocker has blocked the given - Blocked + title: >- + ApplicationLinkOwnerDetails contains the details of a single + application - user. + link owner + title: Addresses of the application links owners pagination: + title: Pagination defines the pagination response type: object properties: next_key: @@ -10986,37 +10532,134 @@ definitions: repeated Bar results = 1; PageResponse page = 2; } - description: |- - QueryBlocksResponse is the response type for the Query/Blocks RPC - method. - desmos.relationships.v1.QueryRelationshipsResponse: + description: >- + QueryApplicationLinkOwnersResponse contains the data returned by the + request + + allowing to get application link owners. + desmos.profiles.v2.QueryApplicationLinkOwnersResponse.ApplicationLinkOwnerDetails: type: object properties: - relationships: + user: + type: string + application: + type: string + username: + type: string + title: |- + ApplicationLinkOwnerDetails contains the details of a single application + link owner + desmos.profiles.v2.QueryApplicationLinksResponse: + type: object + properties: + links: type: array items: type: object properties: - creator: - type: string - title: Creator represents the creator of the relationship - counterparty: + user: type: string - title: >- - Counterparty represents the other user involved in the - relationship - subspace_id: + title: User to which the link is associated + data: + title: Data contains the details of this specific link + type: object + properties: + application: + type: string + title: 'The application name (eg. Twitter, GitHub, etc)' + username: + type: string + title: >- + Username on the application (eg. Twitter tag, GitHub + profile, etc) + state: + title: State of the link type: string - format: uint64 + enum: + - APPLICATION_LINK_STATE_INITIALIZED_UNSPECIFIED + - APPLICATION_LINK_STATE_VERIFICATION_STARTED + - APPLICATION_LINK_STATE_VERIFICATION_ERROR + - APPLICATION_LINK_STATE_VERIFICATION_SUCCESS + - APPLICATION_LINK_STATE_TIMED_OUT + default: APPLICATION_LINK_STATE_INITIALIZED_UNSPECIFIED + description: >- + - APPLICATION_LINK_STATE_INITIALIZED_UNSPECIFIED: A link has + just been initialized + - APPLICATION_LINK_STATE_VERIFICATION_STARTED: A link has just started being verified + - APPLICATION_LINK_STATE_VERIFICATION_ERROR: A link has errored during the verification process + - APPLICATION_LINK_STATE_VERIFICATION_SUCCESS: A link has being verified successfully + - APPLICATION_LINK_STATE_TIMED_OUT: A link has timed out while waiting for the verification + oracle_request: title: >- - SubspaceID represents the id of the subspace for which the - relationship is + OracleRequest represents the request that has been made to the + oracle + type: object + properties: + id: + type: string + format: uint64 + title: ID is the ID of the request + oracle_script_id: + type: string + format: uint64 + title: OracleScriptID is ID of an oracle script + call_data: + title: >- + CallData contains the data used to perform the oracle + request + type: object + properties: + application: + type: string + title: >- + The application for which the ownership should be + verified + call_data: + type: string + title: >- + The hex encoded call data that should be used to verify + the application - valid - description: |- - Relationship is the struct of a relationship. - It represent the concept of "follow" of traditional social networks. + account ownership + client_id: + type: string + title: >- + ClientID represents the ID of the client that has called the + oracle script + result: + title: |- + Data coming from the result of the verification. + Only available when the state is STATE_SUCCESS + type: object + properties: + success: + title: Success represents a successful verification + type: object + properties: + value: + type: string + title: Hex-encoded value that has be signed by the profile + signature: + type: string + title: >- + Hex-encoded signature that has been produced by signing + the value + failed: + title: Failed represents a failed verification + type: object + properties: + error: + type: string + title: Error that is associated with the failure + creation_time: + type: string + format: date-time + title: CreationTime represents the time in which the link was created + title: >- + ApplicationLink contains the data of a link to a centralized + application pagination: + title: Pagination defines the pagination response type: object properties: next_key: @@ -11041,746 +10684,2227 @@ definitions: repeated Bar results = 1; PageResponse page = 2; } - description: |- - QueryRelationshipsResponse is the response type for the - Query/Relationships RPC method. - desmos.relationships.v1.Relationship: + title: |- + QueryApplicationLinksResponse represents the response to the query used + to get the application links for a specific user + desmos.profiles.v2.QueryChainLinkOwnersResponse: type: object properties: - creator: - type: string - title: Creator represents the creator of the relationship - counterparty: - type: string - title: Counterparty represents the other user involved in the relationship - subspace_id: - type: string - format: uint64 - title: >- - SubspaceID represents the id of the subspace for which the - relationship is + owners: + type: array + items: + type: object + properties: + user: + type: string + chain_name: + type: string + target: + type: string + title: >- + ChainLinkOwnerDetails contains the details of a single chain link + owner + title: Addresses of the chain links owners + pagination: + title: Pagination defines the pagination response + type: object + properties: + next_key: + type: string + format: byte + title: |- + next_key is the key to be passed to PageRequest.key to + query the next page most efficiently + total: + type: string + format: uint64 + title: >- + total is total number of results available if + PageRequest.count_total - valid + was set, its value is undefined otherwise + description: |- + PageResponse is to be embedded in gRPC response messages where the + corresponding request message has used PageRequest. + + message SomeResponse { + repeated Bar results = 1; + PageResponse page = 2; + } description: |- - Relationship is the struct of a relationship. - It represent the concept of "follow" of traditional social networks. - desmos.relationships.v1.UserBlock: + QueryChainLinkOwnersResponse contains the data returned by the request + allowing to get chain link owners. + desmos.profiles.v2.QueryChainLinkOwnersResponse.ChainLinkOwnerDetails: type: object properties: - blocker: - type: string - title: Blocker represents the address of the user blocking another one - blocked: + user: type: string - title: Blocked represents the address of the blocked user - reason: + chain_name: type: string - description: Reason represents the optional reason the user has been blocked for. - subspace_id: + target: type: string - format: uint64 - title: >- - SubspaceID represents the ID of the subspace inside which the user - should - - be blocked - description: >- - UserBlock represents the fact that the Blocker has blocked the given - Blocked - - user. - desmos.subspaces.v2.PermissionDetail: + title: ChainLinkOwnerDetails contains the details of a single chain link owner + desmos.profiles.v2.QueryChainLinksResponse: type: object properties: - subspace_id: - type: string - format: uint64 - title: Id of the subspace for which this permission is valid - section_id: - type: integer - format: int64 - title: Id of the section for which this permission is valid - user: - title: User represents a user permission - type: object - properties: - user: - type: string - title: User for which the permission was set - permission: - type: integer - format: int64 - title: Permission set to the user - group: - title: Group represents a group permission - type: object - properties: - group_id: - type: integer - format: int64 - title: Unique id of the group - permission: - type: integer - format: int64 - title: Permission set to the group - title: PermissionDetail contains the details data of a permission - desmos.subspaces.v2.PermissionDetail.Group: - type: object - properties: - group_id: - type: integer - format: int64 - title: Unique id of the group - permission: - type: integer - format: int64 - title: Permission set to the group - title: Group is a permission that has been set to a user group - desmos.subspaces.v2.PermissionDetail.User: - type: object - properties: - user: - type: string - title: User for which the permission was set - permission: - type: integer - format: int64 - title: Permission set to the user - title: User is a permission that has been set to a specific user - desmos.subspaces.v2.QuerySectionResponse: - type: object - properties: - section: - type: object - properties: - subspace_id: - type: string - format: uint64 - title: Id of the subspace inside which the section exists - id: - type: integer - format: int64 - title: Unique id of the section within the subspace - parent_id: - type: integer - format: int64 - title: (optional) Id of the parent section - name: - type: string - title: Name of the section within the subspace - description: - type: string - title: (optional) Description of the section - title: Section contains the data of a single subspace section - title: QuerySectionResponse is the response type for Query/Section RPC method - desmos.subspaces.v2.QuerySectionsResponse: - type: object - properties: - sections: + links: type: array items: type: object properties: - subspace_id: - type: string - format: uint64 - title: Id of the subspace inside which the section exists - id: - type: integer - format: int64 - title: Unique id of the section within the subspace - parent_id: - type: integer - format: int64 - title: (optional) Id of the parent section - name: - type: string - title: Name of the section within the subspace - description: + user: type: string - title: (optional) Description of the section - title: Section contains the data of a single subspace section - pagination: - type: object - properties: - next_key: - type: string - format: byte - title: |- - next_key is the key to be passed to PageRequest.key to - query the next page most efficiently - total: - type: string - format: uint64 - title: >- - total is total number of results available if - PageRequest.count_total + title: User defines the destination profile address to link + address: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type of the + serialized - was set, its value is undefined otherwise - description: |- - PageResponse is to be embedded in gRPC response messages where the - corresponding request message has used PageRequest. + protocol buffer message. This string must contain at least - message SomeResponse { - repeated Bar results = 1; - PageResponse page = 2; - } - title: QuerySectionsResponse is the response type for Query/Sections RPC method - desmos.subspaces.v2.QuerySubspaceResponse: - type: object - properties: - subspace: - type: object - properties: - id: - type: string - format: uint64 - title: Unique id that identifies the subspace - name: - type: string - title: Human-readable name of the subspace - description: - type: string - title: Optional description of this subspace - treasury: - type: string - title: >- - Represents the account that is associated with the subspace and + one "/" character. The last segment of the URL's path must + represent - should be used to connect external applications to verify this - subspace - owner: - type: string - title: Address of the user that owns the subspace - creator: - type: string - title: Address of the subspace creator - creation_time: - type: string - format: date-time - title: the creation time of the subspace - title: Subspace contains all the data of a Desmos subspace - title: QuerySubspaceResponse is the response type for the Query/Subspace method - desmos.subspaces.v2.QuerySubspacesResponse: - type: object - properties: - subspaces: - type: array - items: - type: object - properties: - id: - type: string - format: uint64 - title: Unique id that identifies the subspace - name: - type: string - title: Human-readable name of the subspace - description: - type: string - title: Optional description of this subspace - treasury: - type: string - title: >- - Represents the account that is associated with the subspace and + the fully qualified name of the type (as in - should be used to connect external applications to verify this - subspace - owner: - type: string - title: Address of the user that owns the subspace - creator: - type: string - title: Address of the subspace creator - creation_time: - type: string - format: date-time - title: the creation time of the subspace - title: Subspace contains all the data of a Desmos subspace - pagination: - type: object - properties: - next_key: - type: string - format: byte - title: |- - next_key is the key to be passed to PageRequest.key to - query the next page most efficiently - total: - type: string - format: uint64 - title: >- - total is total number of results available if - PageRequest.count_total + `path/google.protobuf.Duration`). The name should be in a + canonical form - was set, its value is undefined otherwise - description: |- - PageResponse is to be embedded in gRPC response messages where the - corresponding request message has used PageRequest. + (e.g., leading "." is not accepted). - message SomeResponse { - repeated Bar results = 1; - PageResponse page = 2; - } - title: |- - QuerySubspacesResponse is the response type for the Query/Subspaces RPC - method - desmos.subspaces.v2.QueryUserGroupMembersResponse: - type: object - properties: - members: - type: array - items: - type: string - pagination: - type: object - properties: - next_key: - type: string - format: byte - title: |- - next_key is the key to be passed to PageRequest.key to - query the next page most efficiently - total: - type: string - format: uint64 - title: >- - total is total number of results available if - PageRequest.count_total - was set, its value is undefined otherwise - description: |- - PageResponse is to be embedded in gRPC response messages where the - corresponding request message has used PageRequest. + In practice, teams usually precompile into the binary all + types that they - message SomeResponse { - repeated Bar results = 1; - PageResponse page = 2; - } - title: |- - QueryUserGroupMembersResponse is the response type for the - Query/UserGroupMembers RPC method - desmos.subspaces.v2.QueryUserGroupResponse: - type: object - properties: - group: - type: object - properties: - subspace_id: - type: string - format: uint64 - title: ID of the subspace inside which this group exists - section_id: - type: integer - format: int64 - title: (optional) Id of the section inside which this group is valid - id: - type: integer - format: int64 - title: Unique id that identifies the group - name: - type: string - title: Human-readable name of the user group - description: - type: string - title: Optional description of this group - permissions: - type: integer - format: int64 - title: >- - Permissions that will be granted to all the users part of this - group - title: UserGroup represents a group of users - title: |- - QueryUserGroupResponse is the response type for the Query/UserGroup RPC - method - desmos.subspaces.v2.QueryUserGroupsResponse: - type: object - properties: - groups: - type: array - items: - type: object - properties: - subspace_id: - type: string - format: uint64 - title: ID of the subspace inside which this group exists - section_id: - type: integer - format: int64 - title: (optional) Id of the section inside which this group is valid - id: - type: integer - format: int64 - title: Unique id that identifies the group - name: - type: string - title: Human-readable name of the user group - description: - type: string - title: Optional description of this group - permissions: - type: integer - format: int64 - title: >- - Permissions that will be granted to all the users part of this - group - title: UserGroup represents a group of users - pagination: - type: object - properties: - next_key: - type: string - format: byte - title: |- - next_key is the key to be passed to PageRequest.key to - query the next page most efficiently - total: - type: string - format: uint64 - title: >- - total is total number of results available if - PageRequest.count_total + expect it to use in the context of Any. However, for URLs + which use the - was set, its value is undefined otherwise - description: |- - PageResponse is to be embedded in gRPC response messages where the - corresponding request message has used PageRequest. + scheme `http`, `https`, or no scheme, one can optionally set + up a type - message SomeResponse { - repeated Bar results = 1; - PageResponse page = 2; - } - title: |- - QueryUserGroupsResponse is the response type for the Query/UserGroups RPC - method - desmos.subspaces.v2.QueryUserPermissionsResponse: - type: object - properties: - permissions: - type: integer - format: int64 - details: - type: array - items: - type: object - properties: - subspace_id: - type: string - format: uint64 - title: Id of the subspace for which this permission is valid - section_id: - type: integer - format: int64 - title: Id of the section for which this permission is valid - user: - title: User represents a user permission - type: object - properties: - user: - type: string - title: User for which the permission was set - permission: - type: integer - format: int64 - title: Permission set to the user - group: - title: Group represents a group permission - type: object - properties: - group_id: - type: integer - format: int64 - title: Unique id of the group - permission: - type: integer - format: int64 - title: Permission set to the group - title: PermissionDetail contains the details data of a permission - title: |- - QueryUserPermissionsRequest is the response type for the - Query/UserPermissions method - desmos.subspaces.v2.Section: - type: object - properties: - subspace_id: - type: string - format: uint64 - title: Id of the subspace inside which the section exists - id: - type: integer - format: int64 - title: Unique id of the section within the subspace - parent_id: - type: integer - format: int64 - title: (optional) Id of the parent section - name: - type: string - title: Name of the section within the subspace - description: - type: string - title: (optional) Description of the section - title: Section contains the data of a single subspace section - desmos.subspaces.v2.Subspace: - type: object - properties: - id: - type: string - format: uint64 - title: Unique id that identifies the subspace - name: - type: string - title: Human-readable name of the subspace - description: - type: string - title: Optional description of this subspace - treasury: - type: string - title: >- - Represents the account that is associated with the subspace and + server that maps type URLs to message definitions as + follows: - should be used to connect external applications to verify this - subspace - owner: - type: string - title: Address of the user that owns the subspace - creator: - type: string - title: Address of the subspace creator - creation_time: - type: string - format: date-time - title: the creation time of the subspace - title: Subspace contains all the data of a Desmos subspace - desmos.subspaces.v2.UserGroup: - type: object - properties: - subspace_id: - type: string - format: uint64 - title: ID of the subspace inside which this group exists - section_id: - type: integer - format: int64 - title: (optional) Id of the section inside which this group is valid - id: - type: integer - format: int64 - title: Unique id that identifies the group - name: - type: string - title: Human-readable name of the user group - description: - type: string - title: Optional description of this group - permissions: - type: integer - format: int64 - title: Permissions that will be granted to all the users part of this group - title: UserGroup represents a group of users - desmos.posts.v1.Attachment: - type: object - properties: - subspace_id: - type: string - format: uint64 - title: >- - Id of the subspace inside which the post to which this attachment - should be - connected is - section_id: - type: integer - format: int64 - title: >- - Id of the subspace section inside which the post to which this - attachment + * If no scheme is provided, `https` is assumed. - should be connected is - post_id: - type: string - format: uint64 - title: Id of the post to which this attachment should be connected - id: - type: integer - format: int64 - title: If of this attachment - content: - type: object - properties: - type_url: - type: string - description: >- - A URL/resource name that uniquely identifies the type of the - serialized + * An HTTP GET on the URL must yield a + [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based on + the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) - protocol buffer message. This string must contain at least + Note: this functionality is not currently available in the + official - one "/" character. The last segment of the URL's path must - represent + protobuf release, and it is not used for type URLs beginning + with - the fully qualified name of the type (as in + type.googleapis.com. - `path/google.protobuf.Duration`). The name should be in a - canonical form - (e.g., leading "." is not accepted). + Schemes other than `http`, `https` (or the empty scheme) + might be + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above + specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer message + along with a - In practice, teams usually precompile into the binary all types - that they + URL that describes the type of the serialized message. - expect it to use in the context of Any. However, for URLs which - use the - scheme `http`, `https`, or no scheme, one can optionally set up a - type + Protobuf library provides support to pack/unpack Any values in + the form - server that maps type URLs to message definitions as follows: + of utility functions or additional generated methods of the Any + type. - * If no scheme is provided, `https` is assumed. + Example 1: Pack and unpack a message in C++. - * An HTTP GET on the URL must yield a [google.protobuf.Type][] - value in binary format, or produce an error. - * Applications are allowed to cache lookup results based on the - URL, or have them precompiled into a binary to avoid any - lookup. Therefore, binary compatibility needs to be preserved - on changes to types. (Use versioned type names to manage - breaking changes.) + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } - Note: this functionality is not currently available in the - official + Example 2: Pack and unpack a message in Java. - protobuf release, and it is not used for type URLs beginning with + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } - type.googleapis.com. + Example 3: Pack and unpack a message in Python. + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... - Schemes other than `http`, `https` (or the empty scheme) might be + Example 4: Pack and unpack a message in Go - used with implementation specific semantics. - value: - type: string - format: byte - description: >- - Must be a valid serialized protocol buffer of the above specified - type. - description: >- - `Any` contains an arbitrary serialized protocol buffer message along - with a + foo := &pb.Foo{...} + any, err := ptypes.MarshalAny(foo) + ... + foo := &pb.Foo{} + if err := ptypes.UnmarshalAny(any, foo); err != nil { + ... + } - URL that describes the type of the serialized message. + The pack methods provided by protobuf library will by default + use + 'type.googleapis.com/full.type.name' as the type URL and the + unpack - Protobuf library provides support to pack/unpack Any values in the - form + methods only use the fully qualified type name after the last + '/' - of utility functions or additional generated methods of the Any type. + in the type URL, for example "foo.bar.com/x/y.z" will yield type + name "y.z". - Example 1: Pack and unpack a message in C++. - Foo foo = ...; - Any any; - any.PackFrom(foo); - ... - if (any.UnpackTo(&foo)) { - ... - } - Example 2: Pack and unpack a message in Java. + JSON - Foo foo = ...; - Any any = Any.pack(foo); - ... - if (any.is(Foo.class)) { - foo = any.unpack(Foo.class); - } + ==== - Example 3: Pack and unpack a message in Python. + The JSON representation of an `Any` value uses the regular - foo = Foo(...) - any = Any() - any.Pack(foo) - ... - if any.Is(Foo.DESCRIPTOR): - any.Unpack(foo) - ... + representation of the deserialized, embedded message, with an - Example 4: Pack and unpack a message in Go + additional field `@type` which contains the type URL. Example: - foo := &pb.Foo{...} - any, err := ptypes.MarshalAny(foo) - ... - foo := &pb.Foo{} - if err := ptypes.UnmarshalAny(any, foo); err != nil { - ... - } + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } - The pack methods provided by protobuf library will by default use + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } - 'type.googleapis.com/full.type.name' as the type URL and the unpack + If the embedded message type is well-known and has a custom JSON - methods only use the fully qualified type name after the last '/' + representation, that representation will be embedded adding a + field - in the type URL, for example "foo.bar.com/x/y.z" will yield type + `value` which holds the custom JSON in addition to the `@type` - name "y.z". + field. Example (for message [google.protobuf.Duration][]): + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + title: >- + Address contains the data of the external chain address to be + connected + with the Desmos profile + proof: + title: Proof contains the ownership proof of the external chain address + type: object + properties: + pub_key: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type of + the serialized - JSON + protocol buffer message. This string must contain at + least - ==== + one "/" character. The last segment of the URL's path + must represent - The JSON representation of an `Any` value uses the regular + the fully qualified name of the type (as in - representation of the deserialized, embedded message, with an + `path/google.protobuf.Duration`). The name should be in + a canonical form - additional field `@type` which contains the type URL. Example: + (e.g., leading "." is not accepted). - package google.profile; - message Person { - string first_name = 1; - string last_name = 2; - } - { - "@type": "type.googleapis.com/google.profile.Person", - "firstName": , - "lastName": - } + In practice, teams usually precompile into the binary + all types that they - If the embedded message type is well-known and has a custom JSON + expect it to use in the context of Any. However, for + URLs which use the - representation, that representation will be embedded adding a field + scheme `http`, `https`, or no scheme, one can optionally + set up a type - `value` which holds the custom JSON in addition to the `@type` + server that maps type URLs to message definitions as + follows: - field. Example (for message [google.protobuf.Duration][]): - { - "@type": "type.googleapis.com/google.protobuf.Duration", - "value": "1.212s" - } - title: Content of the attachment - title: Attachment contains the data of a single post attachment - desmos.posts.v1.Entities: - type: object - properties: - hashtags: - type: array - items: - type: object - properties: - start: - type: string - format: uint64 - title: Index of the character inside the text at which the tag starts - end: + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a + [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based + on the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently available in + the official + + protobuf release, and it is not used for type URLs + beginning with + + type.googleapis.com. + + + Schemes other than `http`, `https` (or the empty scheme) + might be + + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above + specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer + message along with a + + URL that describes the type of the serialized message. + + + Protobuf library provides support to pack/unpack Any values + in the form + + of utility functions or additional generated methods of the + Any type. + + + Example 1: Pack and unpack a message in C++. + + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } + + Example 2: Pack and unpack a message in Java. + + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + + Example 3: Pack and unpack a message in Python. + + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + + Example 4: Pack and unpack a message in Go + + foo := &pb.Foo{...} + any, err := ptypes.MarshalAny(foo) + ... + foo := &pb.Foo{} + if err := ptypes.UnmarshalAny(any, foo); err != nil { + ... + } + + The pack methods provided by protobuf library will by + default use + + 'type.googleapis.com/full.type.name' as the type URL and the + unpack + + methods only use the fully qualified type name after the + last '/' + + in the type URL, for example "foo.bar.com/x/y.z" will yield + type + + name "y.z". + + + + JSON + + ==== + + The JSON representation of an `Any` value uses the regular + + representation of the deserialized, embedded message, with + an + + additional field `@type` which contains the type URL. + Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a custom + JSON + + representation, that representation will be embedded adding + a field + + `value` which holds the custom JSON in addition to the + `@type` + + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + title: >- + PubKey represents the public key associated with the address + for which to + + prove the ownership + signature: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type of + the serialized + + protocol buffer message. This string must contain at + least + + one "/" character. The last segment of the URL's path + must represent + + the fully qualified name of the type (as in + + `path/google.protobuf.Duration`). The name should be in + a canonical form + + (e.g., leading "." is not accepted). + + + In practice, teams usually precompile into the binary + all types that they + + expect it to use in the context of Any. However, for + URLs which use the + + scheme `http`, `https`, or no scheme, one can optionally + set up a type + + server that maps type URLs to message definitions as + follows: + + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a + [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based + on the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently available in + the official + + protobuf release, and it is not used for type URLs + beginning with + + type.googleapis.com. + + + Schemes other than `http`, `https` (or the empty scheme) + might be + + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above + specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer + message along with a + + URL that describes the type of the serialized message. + + + Protobuf library provides support to pack/unpack Any values + in the form + + of utility functions or additional generated methods of the + Any type. + + + Example 1: Pack and unpack a message in C++. + + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } + + Example 2: Pack and unpack a message in Java. + + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + + Example 3: Pack and unpack a message in Python. + + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + + Example 4: Pack and unpack a message in Go + + foo := &pb.Foo{...} + any, err := ptypes.MarshalAny(foo) + ... + foo := &pb.Foo{} + if err := ptypes.UnmarshalAny(any, foo); err != nil { + ... + } + + The pack methods provided by protobuf library will by + default use + + 'type.googleapis.com/full.type.name' as the type URL and the + unpack + + methods only use the fully qualified type name after the + last '/' + + in the type URL, for example "foo.bar.com/x/y.z" will yield + type + + name "y.z". + + + + JSON + + ==== + + The JSON representation of an `Any` value uses the regular + + representation of the deserialized, embedded message, with + an + + additional field `@type` which contains the type URL. + Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a custom + JSON + + representation, that representation will be embedded adding + a field + + `value` which holds the custom JSON in addition to the + `@type` + + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + title: >- + Signature represents the hex-encoded signature of the + PlainText value + plain_text: + type: string + title: >- + PlainText represents the hex-encoded value signed in order + to produce the + + Signature + chain_config: + title: ChainConfig contains the configuration of the external chain + type: object + properties: + name: + type: string + description: >- + ChainConfig contains the data of the chain with which the link + is made. + creation_time: + type: string + format: date-time + title: >- + CreationTime represents the time in which the link has been + created + title: >- + ChainLink contains the data representing either an inter- or cross- + chain + + link + pagination: + title: Pagination defines the pagination response + type: object + properties: + next_key: + type: string + format: byte + title: |- + next_key is the key to be passed to PageRequest.key to + query the next page most efficiently + total: + type: string + format: uint64 + title: >- + total is total number of results available if + PageRequest.count_total + + was set, its value is undefined otherwise + description: |- + PageResponse is to be embedded in gRPC response messages where the + corresponding request message has used PageRequest. + + message SomeResponse { + repeated Bar results = 1; + PageResponse page = 2; + } + description: |- + QueryChainLinksResponse is the response type for the + Query/ChainLinks RPC method. + desmos.profiles.v2.QueryIncomingDTagTransferRequestsResponse: + type: object + properties: + requests: + type: array + items: + type: object + properties: + dtag_to_trade: + type: string + title: >- + DTagToTrade contains the value of the DTag that should be + transferred from + + the receiver of the request to the sender + sender: + type: string + title: >- + Sender represents the address of the account that sent the + request + receiver: + type: string + title: >- + Receiver represents the receiver of the request that, if + accepted, will + + give to the sender their DTag + title: >- + DTagTransferRequest represent a DTag transfer request between two + users + title: >- + Requests represent the list of all the DTag transfer requests made + towards + + the user + pagination: + title: Pagination defines the pagination response + type: object + properties: + next_key: + type: string + format: byte + title: |- + next_key is the key to be passed to PageRequest.key to + query the next page most efficiently + total: + type: string + format: uint64 + title: >- + total is total number of results available if + PageRequest.count_total + + was set, its value is undefined otherwise + description: |- + PageResponse is to be embedded in gRPC response messages where the + corresponding request message has used PageRequest. + + message SomeResponse { + repeated Bar results = 1; + PageResponse page = 2; + } + description: |- + QueryIncomingDTagTransferRequestsResponse is the response type for the + Query/IncomingDTagTransferRequests RPC method. + desmos.profiles.v2.QueryParamsResponse: + type: object + properties: + params: + type: object + properties: + nickname: + type: object + properties: + min_length: + type: string + format: byte + max_length: + type: string + format: byte + title: >- + NicknameParams defines the parameters related to the profiles + nicknames + dtag: + type: object + properties: + reg_ex: + type: string + min_length: + type: string + format: byte + max_length: + type: string + format: byte + title: DTagParams defines the parameters related to profile DTags + bio: + type: object + properties: + max_length: + type: string + format: byte + title: BioParams defines the parameters related to profile biography + oracle: + type: object + properties: + script_id: + type: string + format: uint64 + title: >- + ScriptID represents the ID of the oracle script to be called + to verify the + + data + ask_count: + type: string + format: uint64 + title: >- + AskCount represents the number of oracles to which ask to + verify the data + min_count: + type: string + format: uint64 + title: >- + MinCount represents the minimum count of oracles that should + complete the + + verification successfully + prepare_gas: + type: string + format: uint64 + title: >- + PrepareGas represents the amount of gas to be used during the + preparation + + stage of the oracle script + execute_gas: + type: string + format: uint64 + title: >- + ExecuteGas represents the amount of gas to be used during the + execution of + + the oracle script + fee_amount: + type: array + items: + type: object + properties: + denom: + type: string + amount: + type: string + description: >- + Coin defines a token with a denomination and an amount. + + + NOTE: The amount field is an Int which implements the custom + method + + signatures required by gogoproto. + title: >- + FeeAmount represents the amount of fees to be payed in order + to execute the + + oracle script + title: |- + OracleParams defines the parameters related to the oracle + that will be used to verify the ownership of a centralized + application account by a Desmos profile + title: Params contains the parameters for the profiles module + description: QueryParamsResponse is the response type for the Query/Params RPC method. + desmos.profiles.v2.QueryProfileResponse: + type: object + properties: + profile: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type of the + serialized + + protocol buffer message. This string must contain at least + + one "/" character. The last segment of the URL's path must + represent + + the fully qualified name of the type (as in + + `path/google.protobuf.Duration`). The name should be in a + canonical form + + (e.g., leading "." is not accepted). + + + In practice, teams usually precompile into the binary all types + that they + + expect it to use in the context of Any. However, for URLs which + use the + + scheme `http`, `https`, or no scheme, one can optionally set up a + type + + server that maps type URLs to message definitions as follows: + + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based on the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently available in the + official + + protobuf release, and it is not used for type URLs beginning with + + type.googleapis.com. + + + Schemes other than `http`, `https` (or the empty scheme) might be + + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above specified + type. + description: >- + `Any` contains an arbitrary serialized protocol buffer message along + with a + + URL that describes the type of the serialized message. + + + Protobuf library provides support to pack/unpack Any values in the + form + + of utility functions or additional generated methods of the Any type. + + + Example 1: Pack and unpack a message in C++. + + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } + + Example 2: Pack and unpack a message in Java. + + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + + Example 3: Pack and unpack a message in Python. + + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + + Example 4: Pack and unpack a message in Go + + foo := &pb.Foo{...} + any, err := ptypes.MarshalAny(foo) + ... + foo := &pb.Foo{} + if err := ptypes.UnmarshalAny(any, foo); err != nil { + ... + } + + The pack methods provided by protobuf library will by default use + + 'type.googleapis.com/full.type.name' as the type URL and the unpack + + methods only use the fully qualified type name after the last '/' + + in the type URL, for example "foo.bar.com/x/y.z" will yield type + + name "y.z". + + + + JSON + + ==== + + The JSON representation of an `Any` value uses the regular + + representation of the deserialized, embedded message, with an + + additional field `@type` which contains the type URL. Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a custom JSON + + representation, that representation will be embedded adding a field + + `value` which holds the custom JSON in addition to the `@type` + + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + description: >- + QueryProfileResponse is the response type for the Query/Profile RPC + method. + desmos.profiles.v2.Result: + type: object + properties: + success: + title: Success represents a successful verification + type: object + properties: + value: + type: string + title: Hex-encoded value that has be signed by the profile + signature: + type: string + title: Hex-encoded signature that has been produced by signing the value + failed: + title: Failed represents a failed verification + type: object + properties: + error: + type: string + title: Error that is associated with the failure + title: Result represents a verification result + desmos.profiles.v2.Result.Failed: + type: object + properties: + error: + type: string + title: Error that is associated with the failure + title: |- + Failed is the result of an application link that has not been verified + successfully + desmos.profiles.v2.Result.Success: + type: object + properties: + value: + type: string + title: Hex-encoded value that has be signed by the profile + signature: + type: string + title: Hex-encoded signature that has been produced by signing the value + title: |- + Success is the result of an application link that has been successfully + verified + google.protobuf.Any: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type of the + serialized + + protocol buffer message. This string must contain at least + + one "/" character. The last segment of the URL's path must represent + + the fully qualified name of the type (as in + + `path/google.protobuf.Duration`). The name should be in a canonical + form + + (e.g., leading "." is not accepted). + + + In practice, teams usually precompile into the binary all types that + they + + expect it to use in the context of Any. However, for URLs which use + the + + scheme `http`, `https`, or no scheme, one can optionally set up a type + + server that maps type URLs to message definitions as follows: + + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based on the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently available in the official + + protobuf release, and it is not used for type URLs beginning with + + type.googleapis.com. + + + Schemes other than `http`, `https` (or the empty scheme) might be + + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above specified + type. + description: >- + `Any` contains an arbitrary serialized protocol buffer message along with + a + + URL that describes the type of the serialized message. + + + Protobuf library provides support to pack/unpack Any values in the form + + of utility functions or additional generated methods of the Any type. + + + Example 1: Pack and unpack a message in C++. + + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } + + Example 2: Pack and unpack a message in Java. + + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + + Example 3: Pack and unpack a message in Python. + + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + + Example 4: Pack and unpack a message in Go + + foo := &pb.Foo{...} + any, err := ptypes.MarshalAny(foo) + ... + foo := &pb.Foo{} + if err := ptypes.UnmarshalAny(any, foo); err != nil { + ... + } + + The pack methods provided by protobuf library will by default use + + 'type.googleapis.com/full.type.name' as the type URL and the unpack + + methods only use the fully qualified type name after the last '/' + + in the type URL, for example "foo.bar.com/x/y.z" will yield type + + name "y.z". + + + + JSON + + ==== + + The JSON representation of an `Any` value uses the regular + + representation of the deserialized, embedded message, with an + + additional field `@type` which contains the type URL. Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a custom JSON + + representation, that representation will be embedded adding a field + + `value` which holds the custom JSON in addition to the `@type` + + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + grpc.gateway.runtime.Error: + type: object + properties: + error: + type: string + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type of the + serialized + + protocol buffer message. This string must contain at least + + one "/" character. The last segment of the URL's path must + represent + + the fully qualified name of the type (as in + + `path/google.protobuf.Duration`). The name should be in a + canonical form + + (e.g., leading "." is not accepted). + + + In practice, teams usually precompile into the binary all types + that they + + expect it to use in the context of Any. However, for URLs which + use the + + scheme `http`, `https`, or no scheme, one can optionally set up + a type + + server that maps type URLs to message definitions as follows: + + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based on the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently available in the + official + + protobuf release, and it is not used for type URLs beginning + with + + type.googleapis.com. + + + Schemes other than `http`, `https` (or the empty scheme) might + be + + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above + specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer message along + with a + + URL that describes the type of the serialized message. + + + Protobuf library provides support to pack/unpack Any values in the + form + + of utility functions or additional generated methods of the Any + type. + + + Example 1: Pack and unpack a message in C++. + + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } + + Example 2: Pack and unpack a message in Java. + + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + + Example 3: Pack and unpack a message in Python. + + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + + Example 4: Pack and unpack a message in Go + + foo := &pb.Foo{...} + any, err := ptypes.MarshalAny(foo) + ... + foo := &pb.Foo{} + if err := ptypes.UnmarshalAny(any, foo); err != nil { + ... + } + + The pack methods provided by protobuf library will by default use + + 'type.googleapis.com/full.type.name' as the type URL and the unpack + + methods only use the fully qualified type name after the last '/' + + in the type URL, for example "foo.bar.com/x/y.z" will yield type + + name "y.z". + + + + JSON + + ==== + + The JSON representation of an `Any` value uses the regular + + representation of the deserialized, embedded message, with an + + additional field `@type` which contains the type URL. Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a custom JSON + + representation, that representation will be embedded adding a field + + `value` which holds the custom JSON in addition to the `@type` + + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + desmos.relationships.v1.QueryBlocksResponse: + type: object + properties: + blocks: + type: array + items: + type: object + properties: + blocker: + type: string + title: Blocker represents the address of the user blocking another one + blocked: + type: string + title: Blocked represents the address of the blocked user + reason: + type: string + description: >- + Reason represents the optional reason the user has been blocked + for. + subspace_id: + type: string + format: uint64 + title: >- + SubspaceID represents the ID of the subspace inside which the + user should + + be blocked + description: >- + UserBlock represents the fact that the Blocker has blocked the given + Blocked + + user. + pagination: + type: object + properties: + next_key: + type: string + format: byte + title: |- + next_key is the key to be passed to PageRequest.key to + query the next page most efficiently + total: + type: string + format: uint64 + title: >- + total is total number of results available if + PageRequest.count_total + + was set, its value is undefined otherwise + description: |- + PageResponse is to be embedded in gRPC response messages where the + corresponding request message has used PageRequest. + + message SomeResponse { + repeated Bar results = 1; + PageResponse page = 2; + } + description: |- + QueryBlocksResponse is the response type for the Query/Blocks RPC + method. + desmos.relationships.v1.QueryRelationshipsResponse: + type: object + properties: + relationships: + type: array + items: + type: object + properties: + creator: + type: string + title: Creator represents the creator of the relationship + counterparty: + type: string + title: >- + Counterparty represents the other user involved in the + relationship + subspace_id: + type: string + format: uint64 + title: >- + SubspaceID represents the id of the subspace for which the + relationship is + + valid + description: |- + Relationship is the struct of a relationship. + It represent the concept of "follow" of traditional social networks. + pagination: + type: object + properties: + next_key: + type: string + format: byte + title: |- + next_key is the key to be passed to PageRequest.key to + query the next page most efficiently + total: + type: string + format: uint64 + title: >- + total is total number of results available if + PageRequest.count_total + + was set, its value is undefined otherwise + description: |- + PageResponse is to be embedded in gRPC response messages where the + corresponding request message has used PageRequest. + + message SomeResponse { + repeated Bar results = 1; + PageResponse page = 2; + } + description: |- + QueryRelationshipsResponse is the response type for the + Query/Relationships RPC method. + desmos.relationships.v1.Relationship: + type: object + properties: + creator: + type: string + title: Creator represents the creator of the relationship + counterparty: + type: string + title: Counterparty represents the other user involved in the relationship + subspace_id: + type: string + format: uint64 + title: >- + SubspaceID represents the id of the subspace for which the + relationship is + + valid + description: |- + Relationship is the struct of a relationship. + It represent the concept of "follow" of traditional social networks. + desmos.relationships.v1.UserBlock: + type: object + properties: + blocker: + type: string + title: Blocker represents the address of the user blocking another one + blocked: + type: string + title: Blocked represents the address of the blocked user + reason: + type: string + description: Reason represents the optional reason the user has been blocked for. + subspace_id: + type: string + format: uint64 + title: >- + SubspaceID represents the ID of the subspace inside which the user + should + + be blocked + description: >- + UserBlock represents the fact that the Blocker has blocked the given + Blocked + + user. + desmos.subspaces.v2.PermissionDetail: + type: object + properties: + subspace_id: + type: string + format: uint64 + title: Id of the subspace for which this permission is valid + section_id: + type: integer + format: int64 + title: Id of the section for which this permission is valid + user: + title: User represents a user permission + type: object + properties: + user: + type: string + title: User for which the permission was set + permission: + type: integer + format: int64 + title: Permission set to the user + group: + title: Group represents a group permission + type: object + properties: + group_id: + type: integer + format: int64 + title: Unique id of the group + permission: + type: integer + format: int64 + title: Permission set to the group + title: PermissionDetail contains the details data of a permission + desmos.subspaces.v2.PermissionDetail.Group: + type: object + properties: + group_id: + type: integer + format: int64 + title: Unique id of the group + permission: + type: integer + format: int64 + title: Permission set to the group + title: Group is a permission that has been set to a user group + desmos.subspaces.v2.PermissionDetail.User: + type: object + properties: + user: + type: string + title: User for which the permission was set + permission: + type: integer + format: int64 + title: Permission set to the user + title: User is a permission that has been set to a specific user + desmos.subspaces.v2.QuerySectionResponse: + type: object + properties: + section: + type: object + properties: + subspace_id: + type: string + format: uint64 + title: Id of the subspace inside which the section exists + id: + type: integer + format: int64 + title: Unique id of the section within the subspace + parent_id: + type: integer + format: int64 + title: (optional) Id of the parent section + name: + type: string + title: Name of the section within the subspace + description: + type: string + title: (optional) Description of the section + title: Section contains the data of a single subspace section + title: QuerySectionResponse is the response type for Query/Section RPC method + desmos.subspaces.v2.QuerySectionsResponse: + type: object + properties: + sections: + type: array + items: + type: object + properties: + subspace_id: + type: string + format: uint64 + title: Id of the subspace inside which the section exists + id: + type: integer + format: int64 + title: Unique id of the section within the subspace + parent_id: + type: integer + format: int64 + title: (optional) Id of the parent section + name: + type: string + title: Name of the section within the subspace + description: + type: string + title: (optional) Description of the section + title: Section contains the data of a single subspace section + pagination: + type: object + properties: + next_key: + type: string + format: byte + title: |- + next_key is the key to be passed to PageRequest.key to + query the next page most efficiently + total: + type: string + format: uint64 + title: >- + total is total number of results available if + PageRequest.count_total + + was set, its value is undefined otherwise + description: |- + PageResponse is to be embedded in gRPC response messages where the + corresponding request message has used PageRequest. + + message SomeResponse { + repeated Bar results = 1; + PageResponse page = 2; + } + title: QuerySectionsResponse is the response type for Query/Sections RPC method + desmos.subspaces.v2.QuerySubspaceResponse: + type: object + properties: + subspace: + type: object + properties: + id: + type: string + format: uint64 + title: Unique id that identifies the subspace + name: + type: string + title: Human-readable name of the subspace + description: + type: string + title: Optional description of this subspace + treasury: + type: string + title: >- + Represents the account that is associated with the subspace and + + should be used to connect external applications to verify this + subspace + owner: + type: string + title: Address of the user that owns the subspace + creator: + type: string + title: Address of the subspace creator + creation_time: + type: string + format: date-time + title: the creation time of the subspace + title: Subspace contains all the data of a Desmos subspace + title: QuerySubspaceResponse is the response type for the Query/Subspace method + desmos.subspaces.v2.QuerySubspacesResponse: + type: object + properties: + subspaces: + type: array + items: + type: object + properties: + id: + type: string + format: uint64 + title: Unique id that identifies the subspace + name: + type: string + title: Human-readable name of the subspace + description: + type: string + title: Optional description of this subspace + treasury: + type: string + title: >- + Represents the account that is associated with the subspace and + + should be used to connect external applications to verify this + subspace + owner: + type: string + title: Address of the user that owns the subspace + creator: + type: string + title: Address of the subspace creator + creation_time: + type: string + format: date-time + title: the creation time of the subspace + title: Subspace contains all the data of a Desmos subspace + pagination: + type: object + properties: + next_key: + type: string + format: byte + title: |- + next_key is the key to be passed to PageRequest.key to + query the next page most efficiently + total: + type: string + format: uint64 + title: >- + total is total number of results available if + PageRequest.count_total + + was set, its value is undefined otherwise + description: |- + PageResponse is to be embedded in gRPC response messages where the + corresponding request message has used PageRequest. + + message SomeResponse { + repeated Bar results = 1; + PageResponse page = 2; + } + title: |- + QuerySubspacesResponse is the response type for the Query/Subspaces RPC + method + desmos.subspaces.v2.QueryUserGroupMembersResponse: + type: object + properties: + members: + type: array + items: + type: string + pagination: + type: object + properties: + next_key: + type: string + format: byte + title: |- + next_key is the key to be passed to PageRequest.key to + query the next page most efficiently + total: + type: string + format: uint64 + title: >- + total is total number of results available if + PageRequest.count_total + + was set, its value is undefined otherwise + description: |- + PageResponse is to be embedded in gRPC response messages where the + corresponding request message has used PageRequest. + + message SomeResponse { + repeated Bar results = 1; + PageResponse page = 2; + } + title: |- + QueryUserGroupMembersResponse is the response type for the + Query/UserGroupMembers RPC method + desmos.subspaces.v2.QueryUserGroupResponse: + type: object + properties: + group: + type: object + properties: + subspace_id: + type: string + format: uint64 + title: ID of the subspace inside which this group exists + section_id: + type: integer + format: int64 + title: (optional) Id of the section inside which this group is valid + id: + type: integer + format: int64 + title: Unique id that identifies the group + name: + type: string + title: Human-readable name of the user group + description: + type: string + title: Optional description of this group + permissions: + type: integer + format: int64 + title: >- + Permissions that will be granted to all the users part of this + group + title: UserGroup represents a group of users + title: |- + QueryUserGroupResponse is the response type for the Query/UserGroup RPC + method + desmos.subspaces.v2.QueryUserGroupsResponse: + type: object + properties: + groups: + type: array + items: + type: object + properties: + subspace_id: + type: string + format: uint64 + title: ID of the subspace inside which this group exists + section_id: + type: integer + format: int64 + title: (optional) Id of the section inside which this group is valid + id: + type: integer + format: int64 + title: Unique id that identifies the group + name: + type: string + title: Human-readable name of the user group + description: + type: string + title: Optional description of this group + permissions: + type: integer + format: int64 + title: >- + Permissions that will be granted to all the users part of this + group + title: UserGroup represents a group of users + pagination: + type: object + properties: + next_key: + type: string + format: byte + title: |- + next_key is the key to be passed to PageRequest.key to + query the next page most efficiently + total: + type: string + format: uint64 + title: >- + total is total number of results available if + PageRequest.count_total + + was set, its value is undefined otherwise + description: |- + PageResponse is to be embedded in gRPC response messages where the + corresponding request message has used PageRequest. + + message SomeResponse { + repeated Bar results = 1; + PageResponse page = 2; + } + title: |- + QueryUserGroupsResponse is the response type for the Query/UserGroups RPC + method + desmos.subspaces.v2.QueryUserPermissionsResponse: + type: object + properties: + permissions: + type: integer + format: int64 + details: + type: array + items: + type: object + properties: + subspace_id: + type: string + format: uint64 + title: Id of the subspace for which this permission is valid + section_id: + type: integer + format: int64 + title: Id of the section for which this permission is valid + user: + title: User represents a user permission + type: object + properties: + user: + type: string + title: User for which the permission was set + permission: + type: integer + format: int64 + title: Permission set to the user + group: + title: Group represents a group permission + type: object + properties: + group_id: + type: integer + format: int64 + title: Unique id of the group + permission: + type: integer + format: int64 + title: Permission set to the group + title: PermissionDetail contains the details data of a permission + title: |- + QueryUserPermissionsRequest is the response type for the + Query/UserPermissions method + desmos.subspaces.v2.Section: + type: object + properties: + subspace_id: + type: string + format: uint64 + title: Id of the subspace inside which the section exists + id: + type: integer + format: int64 + title: Unique id of the section within the subspace + parent_id: + type: integer + format: int64 + title: (optional) Id of the parent section + name: + type: string + title: Name of the section within the subspace + description: + type: string + title: (optional) Description of the section + title: Section contains the data of a single subspace section + desmos.subspaces.v2.Subspace: + type: object + properties: + id: + type: string + format: uint64 + title: Unique id that identifies the subspace + name: + type: string + title: Human-readable name of the subspace + description: + type: string + title: Optional description of this subspace + treasury: + type: string + title: >- + Represents the account that is associated with the subspace and + + should be used to connect external applications to verify this + subspace + owner: + type: string + title: Address of the user that owns the subspace + creator: + type: string + title: Address of the subspace creator + creation_time: + type: string + format: date-time + title: the creation time of the subspace + title: Subspace contains all the data of a Desmos subspace + desmos.subspaces.v2.UserGroup: + type: object + properties: + subspace_id: + type: string + format: uint64 + title: ID of the subspace inside which this group exists + section_id: + type: integer + format: int64 + title: (optional) Id of the section inside which this group is valid + id: + type: integer + format: int64 + title: Unique id that identifies the group + name: + type: string + title: Human-readable name of the user group + description: + type: string + title: Optional description of this group + permissions: + type: integer + format: int64 + title: Permissions that will be granted to all the users part of this group + title: UserGroup represents a group of users + desmos.posts.v1.Attachment: + type: object + properties: + subspace_id: + type: string + format: uint64 + title: >- + Id of the subspace inside which the post to which this attachment + should be + + connected is + section_id: + type: integer + format: int64 + title: >- + Id of the subspace section inside which the post to which this + attachment + + should be connected is + post_id: + type: string + format: uint64 + title: Id of the post to which this attachment should be connected + id: + type: integer + format: int64 + title: If of this attachment + content: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type of the + serialized + + protocol buffer message. This string must contain at least + + one "/" character. The last segment of the URL's path must + represent + + the fully qualified name of the type (as in + + `path/google.protobuf.Duration`). The name should be in a + canonical form + + (e.g., leading "." is not accepted). + + + In practice, teams usually precompile into the binary all types + that they + + expect it to use in the context of Any. However, for URLs which + use the + + scheme `http`, `https`, or no scheme, one can optionally set up a + type + + server that maps type URLs to message definitions as follows: + + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based on the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently available in the + official + + protobuf release, and it is not used for type URLs beginning with + + type.googleapis.com. + + + Schemes other than `http`, `https` (or the empty scheme) might be + + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above specified + type. + description: >- + `Any` contains an arbitrary serialized protocol buffer message along + with a + + URL that describes the type of the serialized message. + + + Protobuf library provides support to pack/unpack Any values in the + form + + of utility functions or additional generated methods of the Any type. + + + Example 1: Pack and unpack a message in C++. + + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } + + Example 2: Pack and unpack a message in Java. + + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + + Example 3: Pack and unpack a message in Python. + + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + + Example 4: Pack and unpack a message in Go + + foo := &pb.Foo{...} + any, err := ptypes.MarshalAny(foo) + ... + foo := &pb.Foo{} + if err := ptypes.UnmarshalAny(any, foo); err != nil { + ... + } + + The pack methods provided by protobuf library will by default use + + 'type.googleapis.com/full.type.name' as the type URL and the unpack + + methods only use the fully qualified type name after the last '/' + + in the type URL, for example "foo.bar.com/x/y.z" will yield type + + name "y.z". + + + + JSON + + ==== + + The JSON representation of an `Any` value uses the regular + + representation of the deserialized, embedded message, with an + + additional field `@type` which contains the type URL. Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a custom JSON + + representation, that representation will be embedded adding a field + + `value` which holds the custom JSON in addition to the `@type` + + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + title: Content of the attachment + title: Attachment contains the data of a single post attachment + desmos.posts.v1.Entities: + type: object + properties: + hashtags: + type: array + items: + type: object + properties: + start: + type: string + format: uint64 + title: Index of the character inside the text at which the tag starts + end: type: string format: uint64 title: Index of the character inside the text at which the tag ends @@ -12794,71 +13918,501 @@ definitions: title: Url contains the details of a generic URL author: type: string - title: Author of the post - conversation_id: + title: Author of the post + conversation_id: + type: string + format: uint64 + title: (optional) Id of the original post of the conversation + referenced_posts: + type: array + items: + type: object + properties: + type: + title: Type of reference + type: string + enum: + - TYPE_UNSPECIFIED + - TYPE_REPLY_TO + - TYPE_QUOTE + - TYPE_REPOST + default: TYPE_UNSPECIFIED + description: |- + - TYPE_UNSPECIFIED: No reference specified + - TYPE_REPLY_TO: This reference represents a reply to the specified post + - TYPE_QUOTE: This reference represents a quote of the specified post + - TYPE_REPOST: This reference represents a repost of the specified post + post_id: + type: string + format: uint64 + title: Id of the referenced post + position: + type: string + format: uint64 + title: >- + Position of the reference inside the post's text. This + should be used only + + with the type set to TYPE_QUOTE + title: PostReference contains the details of a post reference + title: >- + A list this posts references (either as a reply, repost or + quote) + reply_settings: + title: Reply settings of this post + type: string + enum: + - REPLY_SETTING_UNSPECIFIED + - REPLY_SETTING_EVERYONE + - REPLY_SETTING_FOLLOWERS + - REPLY_SETTING_MUTUAL + - REPLY_SETTING_MENTIONS + default: REPLY_SETTING_UNSPECIFIED + description: |- + - REPLY_SETTING_UNSPECIFIED: No reply setting specified + - REPLY_SETTING_EVERYONE: Everyone will be able to reply to this post + - REPLY_SETTING_FOLLOWERS: Only followers of the author will be able to reply to this post + - REPLY_SETTING_MUTUAL: Only the author mutual followers will be able to reply to this post + - REPLY_SETTING_MENTIONS: Only people mentioned inside this post will be able to reply + creation_date: + type: string + format: date-time + title: Creation date of the post + last_edited_date: + type: string + format: date-time + title: (optional) Last edited time of the post + title: Post contains all the information about a single post + pagination: + type: object + properties: + next_key: + type: string + format: byte + title: |- + next_key is the key to be passed to PageRequest.key to + query the next page most efficiently + total: + type: string + format: uint64 + title: >- + total is total number of results available if + PageRequest.count_total + + was set, its value is undefined otherwise + description: |- + PageResponse is to be embedded in gRPC response messages where the + corresponding request message has used PageRequest. + + message SomeResponse { + repeated Bar results = 1; + PageResponse page = 2; + } + title: >- + QuerySubspacePostsResponse is the response type for the + Query/SubspacePosts + + RPC method + desmos.posts.v1.ReplySetting: + type: string + enum: + - REPLY_SETTING_UNSPECIFIED + - REPLY_SETTING_EVERYONE + - REPLY_SETTING_FOLLOWERS + - REPLY_SETTING_MUTUAL + - REPLY_SETTING_MENTIONS + default: REPLY_SETTING_UNSPECIFIED + description: |- + - REPLY_SETTING_UNSPECIFIED: No reply setting specified + - REPLY_SETTING_EVERYONE: Everyone will be able to reply to this post + - REPLY_SETTING_FOLLOWERS: Only followers of the author will be able to reply to this post + - REPLY_SETTING_MUTUAL: Only the author mutual followers will be able to reply to this post + - REPLY_SETTING_MENTIONS: Only people mentioned inside this post will be able to reply + title: ReplySetting contains the possible reply settings that a post can have + desmos.posts.v1.Tag: + type: object + properties: + start: + type: string + format: uint64 + title: Index of the character inside the text at which the tag starts + end: + type: string + format: uint64 + title: Index of the character inside the text at which the tag ends + tag: + type: string + title: 'Tag reference (user address, hashtag value, etc)' + title: Tag represents a generic tag + desmos.posts.v1.Url: + type: object + properties: + start: + type: string + format: uint64 + title: Index of the character inside the text at which the URL starts + end: + type: string + format: uint64 + title: Index of the character inside the text at which the URL ends + url: + type: string + title: Value of the URL where the user should be redirected to + display_url: + type: string + title: (optional) Display value of the URL + title: Url contains the details of a generic URL + desmos.posts.v1.UserAnswer: + type: object + properties: + subspace_id: + type: string + format: uint64 + title: >- + Subspace id inside which the post related to this attachment is + located + section_id: + type: integer + format: int64 + title: Section id inside which the post related to this attachment is located + post_id: + type: string + format: uint64 + title: Id of the post associated to this attachment + poll_id: + type: integer + format: int64 + title: Id of the poll to which this answer is associated + answers_indexes: + type: array + items: + type: integer + format: int64 + title: Indexes of the answers inside the ProvidedAnswers array + user: + type: string + title: Address of the user answering the poll + title: UserAnswer represents a user answer to a poll + desmos.reports.v1.Params: + type: object + properties: + standard_reasons: + type: array + items: + type: object + properties: + id: + type: integer + format: int64 + title: Id of the reason inside the subspace + title: + type: string + title: Title of the reason + description: + type: string + title: >- + (optional) Extended description of the reason and the cases it + applies to + title: >- + StandardReason contains the data of a standard reason that can be + picked and + + used from different subspaces + title: >- + List of available reasons from which new subspaces can pick their + default + + ones + title: Params contains the module parameters + desmos.reports.v1.QueryParamsResponse: + type: object + properties: + params: + type: object + properties: + standard_reasons: + type: array + items: + type: object + properties: + id: + type: integer + format: int64 + title: Id of the reason inside the subspace + title: + type: string + title: Title of the reason + description: + type: string + title: >- + (optional) Extended description of the reason and the cases + it applies to + title: >- + StandardReason contains the data of a standard reason that can + be picked and + + used from different subspaces + title: >- + List of available reasons from which new subspaces can pick their + default + + ones + title: Params contains the module parameters + title: QueryParamsResponse is the response type for Query/Params RPC method + desmos.reports.v1.QueryReasonsResponse: + type: object + properties: + reasons: + type: array + items: + type: object + properties: + subspace_id: + type: string + format: uint64 + title: Id of the subspace for which this reason is valid + id: + type: integer + format: int64 + title: Id of the reason inside the subspace + title: + type: string + title: Title of the reason + description: + type: string + title: >- + (optional) Extended description of the reason and the cases it + applies to + title: Reason contains the data about a reporting reason + pagination: + type: object + properties: + next_key: + type: string + format: byte + title: |- + next_key is the key to be passed to PageRequest.key to + query the next page most efficiently + total: + type: string + format: uint64 + title: >- + total is total number of results available if + PageRequest.count_total + + was set, its value is undefined otherwise + description: |- + PageResponse is to be embedded in gRPC response messages where the + corresponding request message has used PageRequest. + + message SomeResponse { + repeated Bar results = 1; + PageResponse page = 2; + } + title: QueryReasonsResponse is the response type for Query/Reasons RPC method + desmos.reports.v1.QueryReportsResponse: + type: object + properties: + reports: + type: array + items: + type: object + properties: + subspace_id: type: string format: uint64 - title: (optional) Id of the original post of the conversation - referenced_posts: + title: Id of the subspace for which the report has been created + id: + type: string + format: uint64 + title: Id of the report + reasons_ids: type: array items: - type: object - properties: - type: - title: Type of reference - type: string - enum: - - TYPE_UNSPECIFIED - - TYPE_REPLY_TO - - TYPE_QUOTE - - TYPE_REPOST - default: TYPE_UNSPECIFIED - description: |- - - TYPE_UNSPECIFIED: No reference specified - - TYPE_REPLY_TO: This reference represents a reply to the specified post - - TYPE_QUOTE: This reference represents a quote of the specified post - - TYPE_REPOST: This reference represents a repost of the specified post - post_id: - type: string - format: uint64 - title: Id of the referenced post - position: - type: string - format: uint64 - title: >- - Position of the reference inside the post's text. This - should be used only - - with the type set to TYPE_QUOTE - title: PostReference contains the details of a post reference - title: >- - A list this posts references (either as a reply, repost or - quote) - reply_settings: - title: Reply settings of this post + type: integer + format: int64 + title: Id of the reason this report has been created for + message: type: string - enum: - - REPLY_SETTING_UNSPECIFIED - - REPLY_SETTING_EVERYONE - - REPLY_SETTING_FOLLOWERS - - REPLY_SETTING_MUTUAL - - REPLY_SETTING_MENTIONS - default: REPLY_SETTING_UNSPECIFIED - description: |- - - REPLY_SETTING_UNSPECIFIED: No reply setting specified - - REPLY_SETTING_EVERYONE: Everyone will be able to reply to this post - - REPLY_SETTING_FOLLOWERS: Only followers of the author will be able to reply to this post - - REPLY_SETTING_MUTUAL: Only the author mutual followers will be able to reply to this post - - REPLY_SETTING_MENTIONS: Only people mentioned inside this post will be able to reply + title: (optional) Message attached to this report + reporter: + type: string + title: Address of the reporter + target: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type of the + serialized + + protocol buffer message. This string must contain at least + + one "/" character. The last segment of the URL's path must + represent + + the fully qualified name of the type (as in + + `path/google.protobuf.Duration`). The name should be in a + canonical form + + (e.g., leading "." is not accepted). + + + In practice, teams usually precompile into the binary all + types that they + + expect it to use in the context of Any. However, for URLs + which use the + + scheme `http`, `https`, or no scheme, one can optionally set + up a type + + server that maps type URLs to message definitions as + follows: + + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a + [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based on + the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently available in the + official + + protobuf release, and it is not used for type URLs beginning + with + + type.googleapis.com. + + + Schemes other than `http`, `https` (or the empty scheme) + might be + + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above + specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer message + along with a + + URL that describes the type of the serialized message. + + + Protobuf library provides support to pack/unpack Any values in + the form + + of utility functions or additional generated methods of the Any + type. + + + Example 1: Pack and unpack a message in C++. + + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } + + Example 2: Pack and unpack a message in Java. + + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + + Example 3: Pack and unpack a message in Python. + + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + + Example 4: Pack and unpack a message in Go + + foo := &pb.Foo{...} + any, err := ptypes.MarshalAny(foo) + ... + foo := &pb.Foo{} + if err := ptypes.UnmarshalAny(any, foo); err != nil { + ... + } + + The pack methods provided by protobuf library will by default + use + + 'type.googleapis.com/full.type.name' as the type URL and the + unpack + + methods only use the fully qualified type name after the last + '/' + + in the type URL, for example "foo.bar.com/x/y.z" will yield type + + name "y.z". + + + + JSON + + ==== + + The JSON representation of an `Any` value uses the regular + + representation of the deserialized, embedded message, with an + + additional field `@type` which contains the type URL. Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a custom JSON + + representation, that representation will be embedded adding a + field + + `value` which holds the custom JSON in addition to the `@type` + + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + title: Target of the report creation_date: type: string format: date-time - title: Creation date of the post - last_edited_date: - type: string - format: date-time - title: (optional) Last edited time of the post - title: Post contains all the information about a single post + title: Time in which the report was created + title: Report contains the data of a generic report pagination: type: object properties: @@ -12884,91 +14438,235 @@ definitions: repeated Bar results = 1; PageResponse page = 2; } - title: >- - QuerySubspacePostsResponse is the response type for the - Query/SubspacePosts - - RPC method - desmos.posts.v1.ReplySetting: - type: string - enum: - - REPLY_SETTING_UNSPECIFIED - - REPLY_SETTING_EVERYONE - - REPLY_SETTING_FOLLOWERS - - REPLY_SETTING_MUTUAL - - REPLY_SETTING_MENTIONS - default: REPLY_SETTING_UNSPECIFIED - description: |- - - REPLY_SETTING_UNSPECIFIED: No reply setting specified - - REPLY_SETTING_EVERYONE: Everyone will be able to reply to this post - - REPLY_SETTING_FOLLOWERS: Only followers of the author will be able to reply to this post - - REPLY_SETTING_MUTUAL: Only the author mutual followers will be able to reply to this post - - REPLY_SETTING_MENTIONS: Only people mentioned inside this post will be able to reply - title: ReplySetting contains the possible reply settings that a post can have - desmos.posts.v1.Tag: + title: QueryReportsResponse is the response type for Query/Reports RPC method + desmos.reports.v1.Reason: type: object properties: - start: + subspace_id: type: string format: uint64 - title: Index of the character inside the text at which the tag starts - end: + title: Id of the subspace for which this reason is valid + id: + type: integer + format: int64 + title: Id of the reason inside the subspace + title: type: string - format: uint64 - title: Index of the character inside the text at which the tag ends - tag: + title: Title of the reason + description: type: string - title: 'Tag reference (user address, hashtag value, etc)' - title: Tag represents a generic tag - desmos.posts.v1.Url: + title: >- + (optional) Extended description of the reason and the cases it applies + to + title: Reason contains the data about a reporting reason + desmos.reports.v1.Report: type: object properties: - start: + subspace_id: type: string format: uint64 - title: Index of the character inside the text at which the URL starts - end: + title: Id of the subspace for which the report has been created + id: type: string format: uint64 - title: Index of the character inside the text at which the URL ends - url: + title: Id of the report + reasons_ids: + type: array + items: + type: integer + format: int64 + title: Id of the reason this report has been created for + message: type: string - title: Value of the URL where the user should be redirected to - display_url: + title: (optional) Message attached to this report + reporter: type: string - title: (optional) Display value of the URL - title: Url contains the details of a generic URL - desmos.posts.v1.UserAnswer: + title: Address of the reporter + target: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type of the + serialized + + protocol buffer message. This string must contain at least + + one "/" character. The last segment of the URL's path must + represent + + the fully qualified name of the type (as in + + `path/google.protobuf.Duration`). The name should be in a + canonical form + + (e.g., leading "." is not accepted). + + + In practice, teams usually precompile into the binary all types + that they + + expect it to use in the context of Any. However, for URLs which + use the + + scheme `http`, `https`, or no scheme, one can optionally set up a + type + + server that maps type URLs to message definitions as follows: + + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based on the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently available in the + official + + protobuf release, and it is not used for type URLs beginning with + + type.googleapis.com. + + + Schemes other than `http`, `https` (or the empty scheme) might be + + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above specified + type. + description: >- + `Any` contains an arbitrary serialized protocol buffer message along + with a + + URL that describes the type of the serialized message. + + + Protobuf library provides support to pack/unpack Any values in the + form + + of utility functions or additional generated methods of the Any type. + + + Example 1: Pack and unpack a message in C++. + + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } + + Example 2: Pack and unpack a message in Java. + + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + + Example 3: Pack and unpack a message in Python. + + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + + Example 4: Pack and unpack a message in Go + + foo := &pb.Foo{...} + any, err := ptypes.MarshalAny(foo) + ... + foo := &pb.Foo{} + if err := ptypes.UnmarshalAny(any, foo); err != nil { + ... + } + + The pack methods provided by protobuf library will by default use + + 'type.googleapis.com/full.type.name' as the type URL and the unpack + + methods only use the fully qualified type name after the last '/' + + in the type URL, for example "foo.bar.com/x/y.z" will yield type + + name "y.z". + + + + JSON + + ==== + + The JSON representation of an `Any` value uses the regular + + representation of the deserialized, embedded message, with an + + additional field `@type` which contains the type URL. Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a custom JSON + + representation, that representation will be embedded adding a field + + `value` which holds the custom JSON in addition to the `@type` + + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + title: Target of the report + creation_date: + type: string + format: date-time + title: Time in which the report was created + title: Report contains the data of a generic report + desmos.reports.v1.StandardReason: type: object properties: - subspace_id: - type: string - format: uint64 - title: >- - Subspace id inside which the post related to this attachment is - located - section_id: + id: type: integer format: int64 - title: Section id inside which the post related to this attachment is located - post_id: + title: Id of the reason inside the subspace + title: type: string - format: uint64 - title: Id of the post associated to this attachment - poll_id: - type: integer - format: int64 - title: Id of the poll to which this answer is associated - answers_indexes: - type: array - items: - type: integer - format: int64 - title: Indexes of the answers inside the ProvidedAnswers array - user: + title: Title of the reason + description: type: string - title: Address of the user answering the poll - title: UserAnswer represents a user answer to a poll + title: >- + (optional) Extended description of the reason and the cases it applies + to + title: >- + StandardReason contains the data of a standard reason that can be picked + and + + used from different subspaces desmos.fees.v1.MinFee: type: object properties: diff --git a/docs/architecture/adr-011-reports-module.md b/docs/architecture/adr-011-reports-module.md index d995ac4cbd..e846d6c86d 100644 --- a/docs/architecture/adr-011-reports-module.md +++ b/docs/architecture/adr-011-reports-module.md @@ -6,7 +6,7 @@ ## Status -DRAFT +ACCEPTED Implemented ## Abstract @@ -45,13 +45,13 @@ message Report { // Target of the report oneof Target { - UserData user_data = 5; - PostData post_data = 6; + UserTarget user_data = 5; + PostTarget post_data = 6; } } -// UserData contains the data of a report about a user -message UserData { +// UserTarget contains the data of a report about a user +message UserTarget { // Id of the subspace inside which the user has been reported required uint64 subspace_id = 1; @@ -59,8 +59,8 @@ message UserData { required string user = 2; } -// PostData contains the data of a report about a post -message PostData { +// PostTarget contains the data of a report about a post +message PostTarget { // Id of the subspace inside which the reported post is required uint64 subspace_id = 1; @@ -137,8 +137,8 @@ message MsgCreateReport { // Target of the report oneof Target { - UserData user_data = 4; - PostData post_data = 5; + UserTarget user_data = 4; + PostTarget post_data = 5; } } @@ -242,8 +242,8 @@ service Query { message QueryReportsRequest { // Target to query the reports for oneof Target { - UserData user_data = 1; - PostData post_data = 2; + UserTarget user_data = 1; + PostTarget post_data = 2; } // pagination defines an optional pagination for the request. diff --git a/proto/desmos/posts/v1/genesis.proto b/proto/desmos/posts/v1/genesis.proto index c3665ebb79..61dfc44dc0 100644 --- a/proto/desmos/posts/v1/genesis.proto +++ b/proto/desmos/posts/v1/genesis.proto @@ -55,5 +55,5 @@ message ActivePollData { uint64 post_id = 2 [ (gogoproto.customname) = "PostID" ]; uint32 poll_id = 3 [ (gogoproto.customname) = "PollID" ]; google.protobuf.Timestamp end_date = 4 - [ (gogoproto.nullable) = false, (gogoproto.stdtime) = true ]; + [ (gogoproto.nullable) = false, (gogoproto.stdtime) = true ]; } \ No newline at end of file diff --git a/proto/desmos/reports/v1/genesis.proto b/proto/desmos/reports/v1/genesis.proto new file mode 100644 index 0000000000..427f67d28b --- /dev/null +++ b/proto/desmos/reports/v1/genesis.proto @@ -0,0 +1,34 @@ +syntax = "proto3"; +package desmos.reports.v1; + +import "gogoproto/gogo.proto"; +import "google/protobuf/any.proto"; +import "google/protobuf/timestamp.proto"; +import "cosmos_proto/cosmos.proto"; + +import "desmos/reports/v1/models.proto"; + +option go_package = "github.com/desmos-labs/desmos/v3/x/reports/types"; + +// GenesisState defines the reports module's genesis state. +message GenesisState { + option (gogoproto.goproto_getters) = false; + + repeated SubspaceDataEntry subspaces_data = 1 + [ (gogoproto.nullable) = false ]; + repeated Reason reasons = 2 [ (gogoproto.nullable) = false ]; + repeated Report reports = 3 [ (gogoproto.nullable) = false ]; + Params params = 4 [ (gogoproto.nullable) = false ]; +} + +// SubspaceDataEntry contains the data related to a single subspace +message SubspaceDataEntry { + // Id of the subspace to which the data relates + uint64 subspace_id = 1 [ (gogoproto.customname) = "SubspaceID" ]; + + // Id of the next reason inside the subspace + uint32 reason_id = 2 [ (gogoproto.customname) = "ReasonID" ]; + + // Id of the next report inside the subspace + uint64 report_id = 3 [ (gogoproto.customname) = "ReportID" ]; +} \ No newline at end of file diff --git a/proto/desmos/reports/v1/models.proto b/proto/desmos/reports/v1/models.proto new file mode 100644 index 0000000000..b2d883ac66 --- /dev/null +++ b/proto/desmos/reports/v1/models.proto @@ -0,0 +1,120 @@ +syntax = "proto3"; +package desmos.reports.v1; + +import "gogoproto/gogo.proto"; +import "google/protobuf/any.proto"; +import "google/protobuf/timestamp.proto"; +import "cosmos_proto/cosmos.proto"; + +option go_package = "github.com/desmos-labs/desmos/v3/x/reports/types"; + +// Report contains the data of a generic report +message Report { + option (gogoproto.equal) = true; + option (gogoproto.goproto_stringer) = true; + + // Id of the subspace for which the report has been created + uint64 subspace_id = 1 [ + (gogoproto.customname) = "SubspaceID", + (gogoproto.moretags) = "yaml:\"subspace_id\"" + ]; + + // Id of the report + uint64 id = 2 + [ (gogoproto.customname) = "ID", (gogoproto.moretags) = "yaml:\"id\"" ]; + + // Id of the reason this report has been created for + repeated uint32 reasons_ids = 3 [ + (gogoproto.customname) = "ReasonsIDs", + (gogoproto.moretags) = "yaml:\"reasons_ids\"" + ]; + + // (optional) Message attached to this report + string message = 4 [ (gogoproto.moretags) = "yaml:\"message\"" ]; + + // Address of the reporter + string reporter = 5 [ (gogoproto.moretags) = "yaml:\"reporter\"" ]; + + // Target of the report + google.protobuf.Any target = 6 [ (gogoproto.moretags) = "yaml:\"target\"" ]; + + // Time in which the report was created + google.protobuf.Timestamp creation_date = 7 [ + (gogoproto.stdtime) = true, + (gogoproto.nullable) = false, + (gogoproto.moretags) = "yaml:\"creation_date\"" + ]; +} + +// UserTarget contains the data of a report about a user +message UserTarget { + option (gogoproto.equal) = true; + option (gogoproto.goproto_stringer) = true; + + // Address of the reported user + string user = 1 [ (gogoproto.moretags) = "yaml:\"user\"" ]; +} + +// PostTarget contains the data of a report about a post +message PostTarget { + option (gogoproto.equal) = true; + option (gogoproto.goproto_stringer) = true; + + // Id of the reported post + uint64 post_id = 1 [ + (gogoproto.customname) = "PostID", + (gogoproto.moretags) = "yaml:\"post_id\"" + ]; +} + +// Reason contains the data about a reporting reason +message Reason { + option (gogoproto.equal) = true; + option (gogoproto.goproto_stringer) = true; + + // Id of the subspace for which this reason is valid + uint64 subspace_id = 1 [ + (gogoproto.customname) = "SubspaceID", + (gogoproto.moretags) = "yaml:\"subspace_id\"" + ]; + + // Id of the reason inside the subspace + uint32 id = 2 + [ (gogoproto.customname) = "ID", (gogoproto.moretags) = "yaml:\"id\"" ]; + + // Title of the reason + string title = 3 [ (gogoproto.moretags) = "yaml:\"title\"" ]; + + // (optional) Extended description of the reason and the cases it applies to + string description = 4 [ (gogoproto.moretags) = "yaml:\"description\"" ]; +} + +// Params contains the module parameters +message Params { + option (gogoproto.equal) = true; + option (gogoproto.goproto_stringer) = true; + + // List of available reasons from which new subspaces can pick their default + // ones + repeated StandardReason standard_reasons = 1 [ + (gogoproto.nullable) = false, + (gogoproto.moretags) = "yaml:\"standard_reasons\"" + ]; +} + +// StandardReason contains the data of a standard reason that can be picked and +// used from different subspaces +message StandardReason { + option (gogoproto.equal) = true; + option (gogoproto.goproto_stringer) = true; + + // Id of the reason inside the subspace + uint32 id = 1 + [ (gogoproto.customname) = "ID", (gogoproto.moretags) = "yaml:\"id\"" ]; + + // Title of the reason + string title = 2 [ (gogoproto.moretags) = "yaml:\"title\"" ]; + + // (optional) Extended description of the reason and the cases it applies to + string description = 3 [ (gogoproto.moretags) = "yaml:\"description\"" ]; +} \ No newline at end of file diff --git a/proto/desmos/reports/v1/msgs.proto b/proto/desmos/reports/v1/msgs.proto new file mode 100644 index 0000000000..75bc867784 --- /dev/null +++ b/proto/desmos/reports/v1/msgs.proto @@ -0,0 +1,171 @@ +syntax = "proto3"; +package desmos.reports.v1; + +import "gogoproto/gogo.proto"; +import "google/protobuf/any.proto"; +import "google/protobuf/timestamp.proto"; +import "cosmos_proto/cosmos.proto"; + +import "desmos/reports/v1/models.proto"; + +option go_package = "github.com/desmos-labs/desmos/v3/x/reports/types"; + +// Msg defines the reports Msg service. +service Msg { + // CreateReport allows to create a new report + rpc CreateReport(MsgCreateReport) returns (MsgCreateReportResponse); + + // DeleteReport allows to delete an existing report + rpc DeleteReport(MsgDeleteReport) returns (MsgDeleteReportResponse); + + // SupportStandardReason allows to support one of the reasons present inside + // the module params + rpc SupportStandardReason(MsgSupportStandardReason) + returns (MsgSupportStandardReasonResponse); + + // AddReason allows to add a new supported reporting reason + rpc AddReason(MsgAddReason) returns (MsgAddReasonResponse); + + // RemoveReason allows to remove a supported reporting reason + rpc RemoveReason(MsgRemoveReason) returns (MsgRemoveReasonResponse); +} + +// MsgCreateReport represents the message to be used to create a report +message MsgCreateReport { + // Id of the subspace for which the report should be stored + uint64 subspace_id = 1 [ + (gogoproto.customname) = "SubspaceID", + (gogoproto.moretags) = "yaml:\"subspace_id\"" + ]; + + // Id of the reason this report has been created for + repeated uint32 reasons_ids = 2 [ + (gogoproto.customname) = "ReasonsIDs", + (gogoproto.moretags) = "yaml:\"reasons_ids\"" + ]; + + // (optional) Message attached to this report + string message = 3 [ (gogoproto.moretags) = "yaml:\"message\"" ]; + + // Address of the reporter + string reporter = 4 [ (gogoproto.moretags) = "yaml:\"reporter\"" ]; + + // Target of the report + google.protobuf.Any target = 5 [ (gogoproto.moretags) = "yaml:\"target\"" ]; +} + +// MsgCreateReportResponse represents the Msg/CreateReport response type +message MsgCreateReportResponse { + // Id of the newly created report + uint64 report_id = 1 [ + (gogoproto.customname) = "ReportID", + (gogoproto.moretags) = "yaml:\"report_id\"" + ]; + + // Time in which the report was created + google.protobuf.Timestamp creation_date = 2 [ + (gogoproto.stdtime) = true, + (gogoproto.nullable) = false, + (gogoproto.moretags) = "yaml:\"creation_date\"" + ]; +} + +// MsgDeleteReport represents the message to be used when deleting a report +message MsgDeleteReport { + // Id of the subspace that contains the report to be deleted + uint64 subspace_id = 1 [ + (gogoproto.customname) = "SubspaceID", + (gogoproto.moretags) = "yaml:\"subspace_id\"" + ]; + + // Id of the report to be deleted + uint64 report_id = 2 [ + (gogoproto.customname) = "ReportID", + (gogoproto.moretags) = "yaml:\"report_id\"" + ]; + + // Address of the user deleting the report + string signer = 3 [ (gogoproto.moretags) = "yaml:\"signer\"" ]; +} + +// MsgDeleteReportResponse represents the Msg/DeleteReport response type +message MsgDeleteReportResponse {} + +// MsgSupportStandardReason represents the message to be used when wanting to +// support one reason from the module params +message MsgSupportStandardReason { + // Id of the subspace for which to support the reason + uint64 subspace_id = 1 [ + (gogoproto.customname) = "SubspaceID", + (gogoproto.moretags) = "yaml:\"subspace_id\"" + ]; + + // Id of the reason that should be supported + uint32 standard_reason_id = 2 [ + (gogoproto.customname) = "StandardReasonID", + (gogoproto.moretags) = "yaml:\"standard_reason_id\"" + ]; + + // Address of the user signing the message + string signer = 3 [ (gogoproto.moretags) = "yaml:\"signer\"" ]; +} + +// MsgSupportStandardReasonResponse represents the Msg/SupportStandardReason +// response type +message MsgSupportStandardReasonResponse { + // Id of the newly added reason + uint32 reasons_ids = 1 [ + (gogoproto.customname) = "ReasonsID", + (gogoproto.moretags) = "yaml:\"reasons_ids\"" + ]; +} + +// MsgAddReason represents the message to be used when adding a new supported +// reason +message MsgAddReason { + // Id of the subspace for which to add the reason + uint64 subspace_id = 1 [ + (gogoproto.customname) = "SubspaceID", + (gogoproto.moretags) = "yaml:\"subspace_id\"" + ]; + + // Title of the reason + string title = 2 [ (gogoproto.moretags) = "yaml:\"title\"" ]; + + // (optional) Extended description of the reason and the cases it applies to + string description = 3 [ (gogoproto.moretags) = "yaml:\"description\"" ]; + + // Address of the user adding the supported reason + string signer = 4 [ (gogoproto.moretags) = "yaml:\"signer\"" ]; +} + +// MsgAddReasonResponse represents the Msg/AddReason response type +message MsgAddReasonResponse { + // Id of the newly supported reason + uint32 reason_id = 1 [ + (gogoproto.customname) = "ReasonID", + (gogoproto.moretags) = "yaml:\"reason_id\"" + ]; +} + +// MsgRemoveReason represents the message to be used when removing an exiting +// reporting reason +message MsgRemoveReason { + // Id of the subspace from which to remove the reason + uint64 subspace_id = 1 [ + (gogoproto.customname) = "SubspaceID", + (gogoproto.moretags) = "yaml:\"subspace_id\"" + ]; + + // Id of the reason to be deleted + uint32 reason_id = 2 [ + (gogoproto.customname) = "ReasonID", + (gogoproto.moretags) = "yaml:\"reason_id\"" + ]; + + // Address of the user adding the supported reason + string signer = 3 [ (gogoproto.moretags) = "yaml:\"signer\"" ]; +} + +// MsgRemoveReasonResponse represents the Msg/RemoveReason response type +message MsgRemoveReasonResponse {} diff --git a/proto/desmos/reports/v1/query.proto b/proto/desmos/reports/v1/query.proto new file mode 100644 index 0000000000..6df77bbe3c --- /dev/null +++ b/proto/desmos/reports/v1/query.proto @@ -0,0 +1,90 @@ +syntax = "proto3"; +package desmos.reports.v1; + +import "gogoproto/gogo.proto"; +import "google/api/annotations.proto"; +import "google/protobuf/any.proto"; +import "cosmos_proto/cosmos.proto"; +import "cosmos/base/query/v1beta1/pagination.proto"; + +import "desmos/reports/v1/models.proto"; + +option go_package = "github.com/desmos-labs/desmos/v3/x/reports/types"; + +// Query defines the gRPC querier service. +service Query { + // Reports allows to query the reports for a specific target + rpc Reports(QueryReportsRequest) returns (QueryReportsResponse) { + option (google.api.http).get = "/desmos/reports/v1/{subspace_id}/reports"; + } + + // Reasons allows to query the supported reporting reasons for a subspace + rpc Reasons(QueryReasonsRequest) returns (QueryReasonsResponse) { + option (google.api.http).get = "/desmos/reports/v1/{subspace_id}/reasons"; + } + + // Params allows to query the module parameters + rpc Params(QueryParamsRequest) returns (QueryParamsResponse) { + option (google.api.http).get = "/desmos/reports/v1/params"; + } +} + +// QueryReportsResponse is the request type for Query/Reports RPC method +message QueryReportsRequest { + // Id of the subspace to query the reports for + uint64 subspace_id = 1 [ (gogoproto.moretags) = "yaml:\"subspace_id\"" ]; + + // (optional) Target to query the reports for + google.protobuf.Any target = 2 [ (gogoproto.moretags) = "yaml:\"target\"" ]; + + // (optional) User that reported the target. + // This is going to be used only if the target is also specified + string reporter = 3 [ (gogoproto.moretags) = "yaml:\"reporter\"" ]; + + // pagination defines an optional pagination for the request. + cosmos.base.query.v1beta1.PageRequest pagination = 4 + [ (gogoproto.moretags) = "yaml:\"pagination\"" ]; +} + +// QueryReportsResponse is the response type for Query/Reports RPC method +message QueryReportsResponse { + repeated Report reports = 1 [ + (gogoproto.nullable) = false, + (gogoproto.moretags) = "yaml:\"reports\"" + ]; + + cosmos.base.query.v1beta1.PageResponse pagination = 2 + [ (gogoproto.moretags) = "yaml:\"pagination\"" ]; +} + +// QueryReasonsRequest is the request type for Query/Reasons RPC method +message QueryReasonsRequest { + // Id of the subspace to query the supported reporting reasons for + uint64 subspace_id = 1 [ (gogoproto.moretags) = "yaml:\"subspace_id\"" ]; + + // pagination defines an optional pagination for the request. + cosmos.base.query.v1beta1.PageRequest pagination = 3 + [ (gogoproto.moretags) = "yaml:\"pagination\"" ]; +} + +// QueryReasonsResponse is the response type for Query/Reasons RPC method +message QueryReasonsResponse { + repeated Reason reasons = 1 [ + (gogoproto.nullable) = false, + (gogoproto.moretags) = "yaml:\"reasons\"" + ]; + + cosmos.base.query.v1beta1.PageResponse pagination = 2 + [ (gogoproto.moretags) = "yaml:\"pagination\"" ]; +} + +// QueryParamsRequest is the request type for Query/Params RPC method +message QueryParamsRequest {} + +// QueryParamsResponse is the response type for Query/Params RPC method +message QueryParamsResponse { + Params params = 1 [ + (gogoproto.nullable) = false, + (gogoproto.moretags) = "yaml:\"params\"" + ]; +} diff --git a/x/posts/keeper/alias_functions.go b/x/posts/keeper/alias_functions.go index 544a6f74d4..1a3d84e247 100644 --- a/x/posts/keeper/alias_functions.go +++ b/x/posts/keeper/alias_functions.go @@ -120,6 +120,16 @@ func (k Keeper) IterateSubspacePosts(ctx sdk.Context, subspaceID uint64, fn func } } +// GetSubspacePosts returns all the posts for the given subspace +func (k Keeper) GetSubspacePosts(ctx sdk.Context, subspaceID uint64) []types.Post { + var posts []types.Post + k.IterateSubspacePosts(ctx, subspaceID, func(post types.Post) (stop bool) { + posts = append(posts, post) + return false + }) + return posts +} + // -------------------------------------------------------------------------------------------------------------------- // IterateActivePolls iterates over the polls in the active polls queue and performs the provided function diff --git a/x/posts/keeper/subspaces_hooks.go b/x/posts/keeper/external_hooks.go similarity index 100% rename from x/posts/keeper/subspaces_hooks.go rename to x/posts/keeper/external_hooks.go diff --git a/x/posts/keeper/subspace_hooks_test.go b/x/posts/keeper/external_hooks_test.go similarity index 100% rename from x/posts/keeper/subspace_hooks_test.go rename to x/posts/keeper/external_hooks_test.go diff --git a/x/posts/keeper/genesis.go b/x/posts/keeper/genesis.go index 6e29218643..43a7aec247 100644 --- a/x/posts/keeper/genesis.go +++ b/x/posts/keeper/genesis.go @@ -12,7 +12,7 @@ import ( func (k Keeper) ExportGenesis(ctx sdk.Context) *types.GenesisState { return types.NewGenesisState( k.getSubspaceDataEntries(ctx), - k.getPostData(ctx), + k.getAllPosts(ctx), k.getAllAttachments(ctx), k.getAllActivePollsData(ctx), k.getAllUserAnswers(ctx), @@ -35,8 +35,8 @@ func (k Keeper) getSubspaceDataEntries(ctx sdk.Context) []types.SubspaceDataEntr return entries } -// getPostData returns the posts data stored in the given context -func (k Keeper) getPostData(ctx sdk.Context) []types.GenesisPost { +// getAllPosts returns the posts data stored in the given context +func (k Keeper) getAllPosts(ctx sdk.Context) []types.GenesisPost { var posts []types.GenesisPost k.IteratePosts(ctx, func(post types.Post) (stop bool) { attachmentID, err := k.GetNextAttachmentID(ctx, post.SubspaceID, post.ID) diff --git a/x/posts/simulation/genesis.go b/x/posts/simulation/genesis.go index 4a35cdbb52..5b05f850f8 100644 --- a/x/posts/simulation/genesis.go +++ b/x/posts/simulation/genesis.go @@ -21,9 +21,7 @@ func RandomizeGenState(simState *module.SimulationState) { var subspacesGenesis subspacestypes.GenesisState simState.Cdc.MustUnmarshalJSON(subspacesGenesisBz, &subspacesGenesis) - params := types.NewParams( - RandomMaxTextLength(simState.Rand), - ) + params := types.NewParams(RandomMaxTextLength(simState.Rand)) posts := randomPosts(simState.Rand, subspacesGenesis.Subspaces, simState.Accounts, params) subspacesDataEntries := getSubspacesData(posts) attachments := randomAttachments(simState.Rand, posts) diff --git a/x/posts/simulation/utils.go b/x/posts/simulation/utils.go index d7b72097f6..4a05aa575c 100644 --- a/x/posts/simulation/utils.go +++ b/x/posts/simulation/utils.go @@ -55,6 +55,11 @@ func RandomReplySettings(r *rand.Rand) types.ReplySetting { return allowedReplySettings[r.Intn(len(allowedReplySettings))] } +// RandomGenesisPost returns a random post from the slice given +func RandomGenesisPost(r *rand.Rand, posts []types.GenesisPost) types.GenesisPost { + return posts[r.Intn(len(posts))] +} + // RandomPost returns a random post from the slice given func RandomPost(r *rand.Rand, posts []types.Post) types.Post { return posts[r.Intn(len(posts))] diff --git a/x/posts/types/hooks.go b/x/posts/types/hooks.go index 40f747dcfb..60f3745414 100644 --- a/x/posts/types/hooks.go +++ b/x/posts/types/hooks.go @@ -25,3 +25,61 @@ type PostsHooks interface { AfterPollVotingPeriodEnded(ctx sdk.Context, subspaceID uint64, postID uint64, pollID uint32) // Must be called when a poll tally results are saved } + +// -------------------------------------------------------------------------------------------------------------------- + +// MultiPostsHooks combines multiple posts hooks, all hook functions are run in array sequence +type MultiPostsHooks []PostsHooks + +func NewMultiPostsHooks(hooks ...PostsHooks) MultiPostsHooks { + return hooks +} + +// AfterPostSaved implements PostsHooks +func (h MultiPostsHooks) AfterPostSaved(ctx sdk.Context, subspaceID uint64, postID uint64) { + for _, hook := range h { + hook.AfterPostSaved(ctx, subspaceID, postID) + } +} + +// AfterPostDeleted implements PostsHooks +func (h MultiPostsHooks) AfterPostDeleted(ctx sdk.Context, subspaceID uint64, postID uint64) { + for _, hook := range h { + hook.AfterPostDeleted(ctx, subspaceID, postID) + } +} + +// AfterAttachmentSaved implements PostsHooks +func (h MultiPostsHooks) AfterAttachmentSaved(ctx sdk.Context, subspaceID uint64, postID uint64, attachmentID uint32) { + for _, hook := range h { + hook.AfterAttachmentSaved(ctx, subspaceID, postID, attachmentID) + } +} + +// AfterAttachmentDeleted implements PostsHooks +func (h MultiPostsHooks) AfterAttachmentDeleted(ctx sdk.Context, subspaceID uint64, postID uint64, attachmentID uint32) { + for _, hook := range h { + hook.AfterAttachmentDeleted(ctx, subspaceID, postID, attachmentID) + } +} + +// AfterPollAnswerSaved implements PostsHooks +func (h MultiPostsHooks) AfterPollAnswerSaved(ctx sdk.Context, subspaceID uint64, postID uint64, pollID uint32, user string) { + for _, hook := range h { + hook.AfterPollAnswerSaved(ctx, subspaceID, postID, pollID, user) + } +} + +// AfterPollAnswerDeleted implements PostsHooks +func (h MultiPostsHooks) AfterPollAnswerDeleted(ctx sdk.Context, subspaceID uint64, postID uint64, pollID uint32, user string) { + for _, hook := range h { + hook.AfterPollAnswerDeleted(ctx, subspaceID, postID, pollID, user) + } +} + +// AfterPollVotingPeriodEnded implements PostsHooks +func (h MultiPostsHooks) AfterPollVotingPeriodEnded(ctx sdk.Context, subspaceID uint64, postID uint64, pollID uint32) { + for _, hook := range h { + hook.AfterPollVotingPeriodEnded(ctx, subspaceID, postID, pollID) + } +} diff --git a/x/posts/types/models_test.go b/x/posts/types/models_test.go index 00020b9ca1..36e5668628 100644 --- a/x/posts/types/models_test.go +++ b/x/posts/types/models_test.go @@ -290,8 +290,8 @@ func TestPost_Validate(t *testing.T) { name: "invalid reference position returns error", post: types.NewPost( 1, + 0, 2, - 1, "External id", "Text", "cosmos1eqpa6mv2jgevukaqtjmx5535vhc3mm3cf458zg", diff --git a/x/reports/client/cli/cli_test.go b/x/reports/client/cli/cli_test.go new file mode 100644 index 0000000000..ca228b3ac4 --- /dev/null +++ b/x/reports/client/cli/cli_test.go @@ -0,0 +1,692 @@ +package cli_test + +import ( + "fmt" + "testing" + "time" + + "github.com/cosmos/cosmos-sdk/client/flags" + clitestutil "github.com/cosmos/cosmos-sdk/testutil/cli" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/gogo/protobuf/proto" + tmcli "github.com/tendermint/tendermint/libs/cli" + + poststypes "github.com/desmos-labs/desmos/v3/x/posts/types" + "github.com/desmos-labs/desmos/v3/x/reports/client/cli" + subspacestypes "github.com/desmos-labs/desmos/v3/x/subspaces/types" + + "github.com/cosmos/cosmos-sdk/testutil/network" + "github.com/stretchr/testify/suite" + + "github.com/desmos-labs/desmos/v3/testutil" + "github.com/desmos-labs/desmos/v3/x/reports/types" +) + +func TestIntegrationTestSuite(t *testing.T) { + suite.Run(t, new(IntegrationTestSuite)) +} + +type IntegrationTestSuite struct { + suite.Suite + + cfg network.Config + network *network.Network +} + +func (s *IntegrationTestSuite) SetupSuite() { + s.T().Log("setting up integration test suite") + + cfg := testutil.DefaultConfig() + genesisState := cfg.GenesisState + cfg.NumValidators = 2 + + // Initialize the subspaces module genesis state + subspacesGenesis := subspacestypes.NewGenesisState( + 2, + []subspacestypes.SubspaceData{ + subspacestypes.NewSubspaceData(1, 1, 1), + }, + []subspacestypes.Subspace{ + subspacestypes.NewSubspace( + 1, + "Test subspace", + "This is a test subspace", + "cosmos1s0he0z3g92zwsxdj83h0ky9w463sx7gq9mqtgn", + "cosmos1s0he0z3g92zwsxdj83h0ky9w463sx7gq9mqtgn", + "cosmos1s0he0z3g92zwsxdj83h0ky9w463sx7gq9mqtgn", + time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC), + ), + }, + nil, nil, nil, nil, + ) + subspacesDataBz, err := cfg.Codec.MarshalJSON(subspacesGenesis) + s.Require().NoError(err) + genesisState[subspacestypes.ModuleName] = subspacesDataBz + + // Initialize the posts module genesis state + postsGenesis := poststypes.NewGenesisState( + []poststypes.SubspaceDataEntry{ + poststypes.NewSubspaceDataEntry(1, 2), + }, + []poststypes.GenesisPost{ + poststypes.NewGenesisPost(2, poststypes.NewPost( + 1, + 0, + 1, + "External ID", + "This is a text", + "cosmos13t6y2nnugtshwuy0zkrq287a95lyy8vzleaxmd", + 0, + nil, + []poststypes.PostReference{}, + poststypes.REPLY_SETTING_EVERYONE, + time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC), + nil, + )), + }, + nil, + nil, + nil, + poststypes.DefaultParams(), + ) + postsDataBz, err := cfg.Codec.MarshalJSON(postsGenesis) + s.Require().NoError(err) + genesisState[poststypes.ModuleName] = postsDataBz + + // Initialize the module genesis data + genesis := types.NewGenesisState( + []types.SubspaceDataEntry{ + types.NewSubspacesDataEntry(1, 3, 3), + }, + []types.Reason{ + types.NewReason( + 1, + 1, + "Spam", + "This content is spam", + ), + types.NewReason( + 1, + 2, + "Harmful content", + "This content contains self-harm/suicide", + ), + }, + []types.Report{ + types.NewReport( + 1, + 1, + []uint32{1}, + "This user is a spammer", + types.NewUserTarget("cosmos1pjffdtweghpyxru9alssyqtdkq8mn6sepgstgm"), + "cosmos1zkmf50jq4lzvhvp5ekl0sdf2p4g3v9v8edt24z", + time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC), + ), + types.NewReport( + 1, + 2, + []uint32{1}, + "This content is spam", + types.NewPostTarget(1), + "cosmos1zkmf50jq4lzvhvp5ekl0sdf2p4g3v9v8edt24z", + time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC), + ), + }, + types.NewParams([]types.StandardReason{ + types.NewStandardReason(1, "Spam", "This content is spam/This user is a spammer"), + }), + ) + + // Store the genesis data + reportsGenesisBz, err := cfg.Codec.MarshalJSON(genesis) + s.Require().NoError(err) + genesisState[types.ModuleName] = reportsGenesisBz + cfg.GenesisState = genesisState + + s.cfg = cfg + s.network = network.New(s.T(), cfg) + + _, err = s.network.WaitForHeight(1) + s.Require().NoError(err) +} + +func (s *IntegrationTestSuite) TearDownSuite() { + s.T().Log("tearing down integration test suite") + s.network.Cleanup() +} + +// -------------------------------------------------------------------------------------------------------------------- + +func (s *IntegrationTestSuite) TestCmdQueryUserReports() { + val := s.network.Validators[0] + testCases := []struct { + name string + args []string + shouldErr bool + expResponse types.QueryReportsResponse + }{ + { + name: "reports are returned correctly", + args: []string{ + "1", "cosmos1pjffdtweghpyxru9alssyqtdkq8mn6sepgstgm", + fmt.Sprintf("--%s=%s", cli.FlagReporter, "cosmos1zkmf50jq4lzvhvp5ekl0sdf2p4g3v9v8edt24z"), + fmt.Sprintf("--%s=%d", flags.FlagLimit, 1), + fmt.Sprintf("--%s=%d", flags.FlagPage, 1), + fmt.Sprintf("--%s=json", tmcli.OutputFlag), + }, + shouldErr: false, + expResponse: types.QueryReportsResponse{ + Reports: []types.Report{ + types.NewReport( + 1, + 1, + []uint32{1}, + "This user is a spammer", + types.NewUserTarget("cosmos1pjffdtweghpyxru9alssyqtdkq8mn6sepgstgm"), + "cosmos1zkmf50jq4lzvhvp5ekl0sdf2p4g3v9v8edt24z", + time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC), + ), + }, + }, + }, + } + + for _, tc := range testCases { + tc := tc + s.Run(tc.name, func() { + cmd := cli.GetCmdQueryUserReports() + clientCtx := val.ClientCtx + out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args) + + if tc.shouldErr { + s.Require().Error(err) + } else { + s.Require().NoError(err) + + var response types.QueryReportsResponse + s.Require().NoError(clientCtx.JSONCodec.UnmarshalJSON(out.Bytes(), &response), out.String()) + s.Require().Equal(response.Reports, tc.expResponse.Reports) + } + }) + } +} + +func (s *IntegrationTestSuite) TestCmdQueryPostReports() { + val := s.network.Validators[0] + testCases := []struct { + name string + args []string + shouldErr bool + expResponse types.QueryReportsResponse + }{ + { + name: "reports are returned correctly", + args: []string{ + "1", "1", + fmt.Sprintf("--%s=%s", cli.FlagReporter, "cosmos1zkmf50jq4lzvhvp5ekl0sdf2p4g3v9v8edt24z"), + fmt.Sprintf("--%s=%d", flags.FlagLimit, 1), + fmt.Sprintf("--%s=%d", flags.FlagPage, 1), + fmt.Sprintf("--%s=json", tmcli.OutputFlag), + }, + shouldErr: false, + expResponse: types.QueryReportsResponse{ + Reports: []types.Report{ + types.NewReport( + 1, + 2, + []uint32{1}, + "This content is spam", + types.NewPostTarget(1), + "cosmos1zkmf50jq4lzvhvp5ekl0sdf2p4g3v9v8edt24z", + time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC), + ), + }, + }, + }, + } + + for _, tc := range testCases { + tc := tc + s.Run(tc.name, func() { + cmd := cli.GetCmdQueryPostReports() + clientCtx := val.ClientCtx + out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args) + + if tc.shouldErr { + s.Require().Error(err) + } else { + s.Require().NoError(err) + + var response types.QueryReportsResponse + s.Require().NoError(clientCtx.JSONCodec.UnmarshalJSON(out.Bytes(), &response), out.String()) + s.Require().Equal(response.Reports, tc.expResponse.Reports) + } + }) + } +} + +func (s *IntegrationTestSuite) TestCmdQueryReports() { + val := s.network.Validators[0] + testCases := []struct { + name string + args []string + shouldErr bool + expResponse types.QueryReportsResponse + }{ + { + name: "reports are returned correctly", + args: []string{ + "1", + fmt.Sprintf("--%s=%d", flags.FlagLimit, 1), + fmt.Sprintf("--%s=%d", flags.FlagPage, 1), + fmt.Sprintf("--%s=json", tmcli.OutputFlag), + }, + shouldErr: false, + expResponse: types.QueryReportsResponse{ + Reports: []types.Report{ + types.NewReport( + 1, + 1, + []uint32{1}, + "This user is a spammer", + types.NewUserTarget("cosmos1pjffdtweghpyxru9alssyqtdkq8mn6sepgstgm"), + "cosmos1zkmf50jq4lzvhvp5ekl0sdf2p4g3v9v8edt24z", + time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC), + ), + }, + }, + }, + } + + for _, tc := range testCases { + tc := tc + s.Run(tc.name, func() { + cmd := cli.GetCmdQueryReports() + clientCtx := val.ClientCtx + out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args) + + if tc.shouldErr { + s.Require().Error(err) + } else { + s.Require().NoError(err) + + var response types.QueryReportsResponse + s.Require().NoError(clientCtx.JSONCodec.UnmarshalJSON(out.Bytes(), &response), out.String()) + s.Require().Equal(response.Reports, tc.expResponse.Reports) + } + }) + } +} + +func (s *IntegrationTestSuite) TestCmdQueryReasons() { + val := s.network.Validators[0] + testCases := []struct { + name string + args []string + shouldErr bool + expResponse types.QueryReasonsResponse + }{ + { + name: "reasons are returned correctly", + args: []string{ + "1", + fmt.Sprintf("--%s=%d", flags.FlagLimit, 1), + fmt.Sprintf("--%s=%d", flags.FlagPage, 1), + fmt.Sprintf("--%s=json", tmcli.OutputFlag), + }, + shouldErr: false, + expResponse: types.QueryReasonsResponse{ + Reasons: []types.Reason{ + types.NewReason( + 1, + 1, + "Spam", + "This content is spam", + ), + }, + }, + }, + } + + for _, tc := range testCases { + tc := tc + s.Run(tc.name, func() { + cmd := cli.GetCmdQueryReasons() + clientCtx := val.ClientCtx + out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args) + + if tc.shouldErr { + s.Require().Error(err) + } else { + s.Require().NoError(err) + + var response types.QueryReasonsResponse + s.Require().NoError(clientCtx.JSONCodec.UnmarshalJSON(out.Bytes(), &response), out.String()) + s.Require().Equal(response.Reasons, tc.expResponse.Reasons) + } + }) + } +} + +func (s *IntegrationTestSuite) TestCmdQueryParams() { + val := s.network.Validators[0] + testCases := []struct { + name string + args []string + shouldErr bool + expResponse types.QueryParamsResponse + }{ + { + name: "params are returned correctly", + args: []string{ + fmt.Sprintf("--%s=json", tmcli.OutputFlag), + }, + shouldErr: false, + expResponse: types.QueryParamsResponse{ + Params: types.NewParams([]types.StandardReason{ + types.NewStandardReason(1, "Spam", "This content is spam/This user is a spammer"), + }), + }, + }, + } + + for _, tc := range testCases { + tc := tc + s.Run(tc.name, func() { + cmd := cli.GetCmdQueryParams() + clientCtx := val.ClientCtx + out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args) + + if tc.shouldErr { + s.Require().Error(err) + } else { + s.Require().NoError(err) + + var response types.QueryParamsResponse + s.Require().NoError(clientCtx.JSONCodec.UnmarshalJSON(out.Bytes(), &response), out.String()) + s.Require().Equal(response.Params, tc.expResponse.Params) + } + }) + } +} + +func (s *IntegrationTestSuite) TestCmdReportUser() { + val := s.network.Validators[0] + testCases := []struct { + name string + args []string + shouldErr bool + respType proto.Message + }{ + { + name: "invalid subspace id returns error", + args: []string{ + "", "cosmos1wprgptc8ktt0eemrn2znpxv8crdxm8tdpkdr7w", "1", + }, + shouldErr: true, + }, + { + name: "invalid user address returns error", + args: []string{ + "1", "invalid-address", "1", + }, + shouldErr: true, + }, + { + name: "invalid reason id returns error", + args: []string{ + "1", "cosmos1wprgptc8ktt0eemrn2znpxv8crdxm8tdpkdr7w", "0", + }, + shouldErr: true, + }, + { + name: "valid data returns no error", + args: []string{ + "1", "cosmos1wprgptc8ktt0eemrn2znpxv8crdxm8tdpkdr7w", "1,2,3", + fmt.Sprintf("--%s=%s", cli.FlagMessage, "This is a new report"), + fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address.String()), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), + }, + shouldErr: false, + respType: &sdk.TxResponse{}, + }, + } + + for _, tc := range testCases { + tc := tc + s.Run(tc.name, func() { + cmd := cli.GetCmdReportUser() + clientCtx := val.ClientCtx + + out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args) + if tc.shouldErr { + s.Require().Error(err) + } else { + s.Require().NoError(err) + s.Require().NoError(clientCtx.JSONCodec.UnmarshalJSON(out.Bytes(), tc.respType), out.String()) + } + }) + } +} + +func (s *IntegrationTestSuite) TestCmdReportPost() { + val := s.network.Validators[0] + testCases := []struct { + name string + args []string + shouldErr bool + respType proto.Message + }{ + { + name: "invalid subspace id returns error", + args: []string{ + "", "1", "cosmos1wprgptc8ktt0eemrn2znpxv8crdxm8tdpkdr7w", + }, + shouldErr: true, + }, + { + name: "invalid post id returns error", + args: []string{ + "1", "0", "1", + }, + shouldErr: true, + }, + { + name: "invalid reason id returns error", + args: []string{ + "1", "1", "0", + }, + shouldErr: true, + }, + { + name: "valid data returns no error", + args: []string{ + "1", "1", "1,2,3", + fmt.Sprintf("--%s=%s", cli.FlagMessage, "This is a new report"), + fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address.String()), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), + }, + shouldErr: false, + respType: &sdk.TxResponse{}, + }, + } + + for _, tc := range testCases { + tc := tc + s.Run(tc.name, func() { + cmd := cli.GetCmdReportPost() + clientCtx := val.ClientCtx + + out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args) + if tc.shouldErr { + s.Require().Error(err) + } else { + s.Require().NoError(err) + s.Require().NoError(clientCtx.JSONCodec.UnmarshalJSON(out.Bytes(), tc.respType), out.String()) + } + }) + } +} + +func (s *IntegrationTestSuite) TestCmdDeleteReport() { + val := s.network.Validators[0] + testCases := []struct { + name string + args []string + shouldErr bool + respType proto.Message + }{ + { + name: "invalid subspace id returns error", + args: []string{ + "0", "1", + }, + shouldErr: true, + }, + { + name: "invalid report id returns error", + args: []string{ + "1", "0", + }, + shouldErr: true, + }, + { + name: "valid data returns no error", + args: []string{ + "1", "1", + fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address.String()), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), + }, + shouldErr: false, + respType: &sdk.TxResponse{}, + }, + } + + for _, tc := range testCases { + tc := tc + s.Run(tc.name, func() { + cmd := cli.GetCmdDeleteReport() + clientCtx := val.ClientCtx + + out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args) + if tc.shouldErr { + s.Require().Error(err) + } else { + s.Require().NoError(err) + s.Require().NoError(clientCtx.JSONCodec.UnmarshalJSON(out.Bytes(), tc.respType), out.String()) + } + }) + } +} + +func (s *IntegrationTestSuite) TestCmdSupportStandardReason() { + val := s.network.Validators[0] + testCases := []struct { + name string + args []string + shouldErr bool + respType proto.Message + }{ + { + name: "invalid subspace id returns error", + args: []string{ + "0", "1", + }, + shouldErr: true, + }, + { + name: "invalid reason id returns error", + args: []string{ + "1", "0", + }, + shouldErr: true, + }, + { + name: "valid data returns no error", + args: []string{ + "1", "1", + fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address.String()), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), + }, + shouldErr: false, + respType: &sdk.TxResponse{}, + }, + } + + for _, tc := range testCases { + tc := tc + s.Run(tc.name, func() { + cmd := cli.GetCmdSupportStandardReason() + clientCtx := val.ClientCtx + + out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args) + if tc.shouldErr { + s.Require().Error(err) + } else { + s.Require().NoError(err) + s.Require().NoError(clientCtx.JSONCodec.UnmarshalJSON(out.Bytes(), tc.respType), out.String()) + } + }) + } +} + +func (s *IntegrationTestSuite) TGetCmdAddReason() { + val := s.network.Validators[0] + testCases := []struct { + name string + args []string + shouldErr bool + respType proto.Message + }{ + { + name: "invalid subspace id returns error", + args: []string{ + "0", "Spam", + }, + shouldErr: true, + }, + { + name: "invalid title returns error", + args: []string{ + "1", "", + }, + shouldErr: true, + }, + { + name: "valid data returns no error", + args: []string{ + "1", "Spam", "This content is spam", + fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address.String()), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), + }, + shouldErr: false, + respType: &sdk.TxResponse{}, + }, + } + + for _, tc := range testCases { + tc := tc + s.Run(tc.name, func() { + cmd := cli.GetCmdAddReason() + clientCtx := val.ClientCtx + + out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args) + if tc.shouldErr { + s.Require().Error(err) + } else { + s.Require().NoError(err) + s.Require().NoError(clientCtx.JSONCodec.UnmarshalJSON(out.Bytes(), tc.respType), out.String()) + } + }) + } +} diff --git a/x/reports/client/cli/query.go b/x/reports/client/cli/query.go new file mode 100644 index 0000000000..e1dc048efd --- /dev/null +++ b/x/reports/client/cli/query.go @@ -0,0 +1,266 @@ +package cli + +// DONTCOVER + +import ( + "context" + "fmt" + + poststypes "github.com/desmos-labs/desmos/v3/x/posts/types" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/version" + "github.com/spf13/cobra" + + "github.com/desmos-labs/desmos/v3/x/reports/types" + subspacestypes "github.com/desmos-labs/desmos/v3/x/subspaces/types" +) + +const ( + FlagReporter = "reporter" +) + +// GetQueryCmd returns the command allowing to perform queries +func GetQueryCmd() *cobra.Command { + subspaceQueryCmd := &cobra.Command{ + Use: types.ModuleName, + Short: "Querying commands for the reports module", + DisableFlagParsing: true, + SuggestionsMinimumDistance: 2, + RunE: client.ValidateCmd, + } + subspaceQueryCmd.AddCommand( + GetCmdQueryUserReports(), + GetCmdQueryPostReports(), + GetCmdQueryReports(), + GetCmdQueryReasons(), + GetCmdQueryParams(), + ) + return subspaceQueryCmd +} + +// GetCmdQueryUserReports returns the command to query the reports associated to a user +func GetCmdQueryUserReports() *cobra.Command { + cmd := &cobra.Command{ + Use: "user-reports [subspace-id] [user-address]", + Short: "Query the reports made towards the specified user with an optional reporter", + Example: fmt.Sprintf(` +%s query reports user-reports 1 desmos1cs0gu6006rz9wnmltjuhnuz8k3a2wg6jzmmgyu + --reporter desmos1snj93y7ds58uj8xpnkpgjwvultmalsurdgk8uu +`, version.AppName), + Args: cobra.ExactArgs(2), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + queryClient := types.NewQueryClient(clientCtx) + + subspaceID, err := subspacestypes.ParseSubspaceID(args[0]) + if err != nil { + return err + } + + userAddr, err := sdk.AccAddressFromBech32(args[1]) + if err != nil { + return err + } + + pageReq, err := client.ReadPageRequest(cmd.Flags()) + if err != nil { + return err + } + + reporter, err := cmd.Flags().GetString(FlagReporter) + if err != nil { + return err + } + + res, err := queryClient.Reports(context.Background(), types.NewQueryReportsRequest( + subspaceID, + types.NewUserTarget(userAddr.String()), + reporter, + pageReq, + )) + if err != nil { + return err + } + + return clientCtx.PrintProto(res) + }, + } + + cmd.Flags().String(FlagReporter, "", "Address of the reporter to query the reports for") + + flags.AddQueryFlagsToCmd(cmd) + flags.AddPaginationFlagsToCmd(cmd, "user reports") + + return cmd +} + +// GetCmdQueryPostReports returns the command to query the reports associated to a post +func GetCmdQueryPostReports() *cobra.Command { + cmd := &cobra.Command{ + Use: "post-reports [subspace-id] [post-id]", + Short: "Query the reports made towards the specified post", + Example: fmt.Sprintf(`%s query reports post-reports 1 1`, version.AppName), + Args: cobra.ExactArgs(2), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + queryClient := types.NewQueryClient(clientCtx) + + subspaceID, err := subspacestypes.ParseSubspaceID(args[0]) + if err != nil { + return err + } + + postID, err := poststypes.ParsePostID(args[1]) + if err != nil { + return err + } + + reporter, err := cmd.Flags().GetString(FlagReporter) + if err != nil { + return err + } + + pageReq, err := client.ReadPageRequest(cmd.Flags()) + if err != nil { + return err + } + + res, err := queryClient.Reports(context.Background(), types.NewQueryReportsRequest( + subspaceID, + types.NewPostTarget(postID), + reporter, + pageReq, + )) + if err != nil { + return err + } + + return clientCtx.PrintProto(res) + }, + } + + cmd.Flags().String(FlagReporter, "", "Address of the reporter to query the reports for") + + flags.AddQueryFlagsToCmd(cmd) + flags.AddPaginationFlagsToCmd(cmd, "post reports") + + return cmd +} + +// GetCmdQueryReports returns the command to query the reports of a subspace +func GetCmdQueryReports() *cobra.Command { + cmd := &cobra.Command{ + Use: "reports [subspace-id]", + Short: "Query the reports from within the specified subspace", + Example: fmt.Sprintf(`%s query reports reports 1`, version.AppName), + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + queryClient := types.NewQueryClient(clientCtx) + + subspaceID, err := subspacestypes.ParseSubspaceID(args[0]) + if err != nil { + return err + } + + pageReq, err := client.ReadPageRequest(cmd.Flags()) + if err != nil { + return err + } + + res, err := queryClient.Reports( + context.Background(), + types.NewQueryReportsRequest(subspaceID, nil, "", pageReq), + ) + if err != nil { + return err + } + + return clientCtx.PrintProto(res) + }, + } + + flags.AddQueryFlagsToCmd(cmd) + flags.AddPaginationFlagsToCmd(cmd, "reports") + + return cmd +} + +// GetCmdQueryReasons returns the command to query the reasons of a subspace +func GetCmdQueryReasons() *cobra.Command { + cmd := &cobra.Command{ + Use: "reasons [subspace-id]", + Short: "Query the reasons from within the specified subspace", + Example: fmt.Sprintf(`%s query reports reasons 1`, version.AppName), + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + queryClient := types.NewQueryClient(clientCtx) + + subspaceID, err := subspacestypes.ParseSubspaceID(args[0]) + if err != nil { + return err + } + + pageReq, err := client.ReadPageRequest(cmd.Flags()) + if err != nil { + return err + } + + res, err := queryClient.Reasons(context.Background(), types.NewQueryReasonsRequest(subspaceID, pageReq)) + if err != nil { + return err + } + + return clientCtx.PrintProto(res) + }, + } + + flags.AddQueryFlagsToCmd(cmd) + flags.AddPaginationFlagsToCmd(cmd, "reasons") + + return cmd +} + +// GetCmdQueryParams returns the command to query the params of the module +func GetCmdQueryParams() *cobra.Command { + cmd := &cobra.Command{ + Use: "params", + Short: "Query the module parameters", + Example: fmt.Sprintf(`%s query reports params 1`, version.AppName), + Args: cobra.NoArgs, + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + queryClient := types.NewQueryClient(clientCtx) + + res, err := queryClient.Params(context.Background(), types.NewQueryParamsRequest()) + if err != nil { + return err + } + + return clientCtx.PrintProto(res) + }, + } + + flags.AddQueryFlagsToCmd(cmd) + + return cmd +} diff --git a/x/reports/client/cli/tx.go b/x/reports/client/cli/tx.go new file mode 100644 index 0000000000..a9a727f8fb --- /dev/null +++ b/x/reports/client/cli/tx.go @@ -0,0 +1,339 @@ +package cli + +// DONTCOVER + +import ( + "fmt" + + poststypes "github.com/desmos-labs/desmos/v3/x/posts/types" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/client/tx" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/version" + "github.com/spf13/cobra" + + "github.com/desmos-labs/desmos/v3/x/reports/types" + subspacestypes "github.com/desmos-labs/desmos/v3/x/subspaces/types" +) + +const ( + FlagMessage = "message" +) + +// NewTxCmd returns a new command to perform reports transactions +func NewTxCmd() *cobra.Command { + subspacesTxCmd := &cobra.Command{ + Use: types.ModuleName, + Short: "Reports transaction subcommands", + DisableFlagParsing: true, + SuggestionsMinimumDistance: 2, + RunE: client.ValidateCmd, + } + + subspacesTxCmd.AddCommand( + GetCmdReportUser(), + GetCmdReportPost(), + GetCmdDeleteReport(), + GetCmdSupportStandardReason(), + GetCmdAddReason(), + GetCmdRemoveReason(), + ) + + return subspacesTxCmd +} + +// GetCmdReportUser returns the command allowing to report a user +func GetCmdReportUser() *cobra.Command { + cmd := &cobra.Command{ + Use: "report-user [subspace-id] [user-address] [reasons-ids]", + Args: cobra.ExactArgs(3), + Short: "Report a user, optionally specifying a message", + Long: ` +Report the user inside the specific subspace for the reasons having the given ids. +Multiple reasons can be specified. If so, each reason id must be separated using a comma.`, + Example: fmt.Sprintf(` +%s tx reports report-user 1 desmos1cs0gu6006rz9wnmltjuhnuz8k3a2wg6jzmmgyu 1,2,3 \ + --message "Please admins review this report!" \ + --from alice +`, version.AppName), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } + + subspaceID, err := subspacestypes.ParseSubspaceID(args[0]) + if err != nil { + return err + } + + userAddr, err := sdk.AccAddressFromBech32(args[1]) + if err != nil { + return err + } + + reasons, err := types.ParseReasonsIDs(args[2]) + if err != nil { + return err + } + + message, err := cmd.Flags().GetString(FlagMessage) + if err != nil { + return err + } + + reporter := clientCtx.FromAddress.String() + + msg := types.NewMsgCreateReport( + subspaceID, + reasons, + message, + types.NewUserTarget(userAddr.String()), + reporter, + ) + if err = msg.ValidateBasic(); err != nil { + return fmt.Errorf("message validation failed: %w", err) + } + + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + }, + } + + cmd.Flags().String(FlagMessage, "", "Optional message associated with the report") + + flags.AddTxFlagsToCmd(cmd) + + return cmd +} + +// GetCmdReportPost returns the command allowing to report a post +func GetCmdReportPost() *cobra.Command { + cmd := &cobra.Command{ + Use: "report-post [subspace-id] [post-id] [reasons-ids]", + Args: cobra.ExactArgs(3), + Short: "Report a post, optionally specifying a message", + Long: ` +Report the post having the specified id inside the specific subspace for the reasons having the given ids. +Multiple reasons can be specified. If so, each reason id must be separated using a comma. +`, + Example: fmt.Sprintf(` +%s tx reports report-post 1 1 1,2,3 \ + --message "Please admins review this report!" \ + --from alice +`, version.AppName), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } + + subspaceID, err := subspacestypes.ParseSubspaceID(args[0]) + if err != nil { + return err + } + + postID, err := poststypes.ParsePostID(args[1]) + if err != nil { + return err + } + + reasons, err := types.ParseReasonsIDs(args[2]) + if err != nil { + return err + } + + message, err := cmd.Flags().GetString(FlagMessage) + if err != nil { + return err + } + + reporter := clientCtx.FromAddress.String() + + msg := types.NewMsgCreateReport( + subspaceID, + reasons, + message, + types.NewPostTarget(postID), + reporter, + ) + if err = msg.ValidateBasic(); err != nil { + return fmt.Errorf("message validation failed: %w", err) + } + + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + }, + } + + cmd.Flags().String(FlagMessage, "", "Optional message associated with the report") + + flags.AddTxFlagsToCmd(cmd) + + return cmd +} + +// GetCmdDeleteReport returns the command allowing to delete a report +func GetCmdDeleteReport() *cobra.Command { + cmd := &cobra.Command{ + Use: "delete-report [subspace-id] [report-id]", + Args: cobra.ExactArgs(2), + Short: "Delete a report", + Long: "Delete the report having the given id from the specified subspace", + Example: fmt.Sprintf(`%s tx reports delete-report 1 1 --from alice`, version.AppName), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } + + subspaceID, err := subspacestypes.ParseSubspaceID(args[0]) + if err != nil { + return err + } + + reportID, err := types.ParseReportID(args[1]) + if err != nil { + return err + } + + signer := clientCtx.FromAddress.String() + + msg := types.NewMsgDeleteReport(subspaceID, reportID, signer) + if err = msg.ValidateBasic(); err != nil { + return fmt.Errorf("message validation failed: %w", err) + } + + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + }, + } + + flags.AddTxFlagsToCmd(cmd) + + return cmd +} + +// GetCmdSupportStandardReason returns the command allowing to support a standard reason +func GetCmdSupportStandardReason() *cobra.Command { + cmd := &cobra.Command{ + Use: "support-standard-reason [subspace-id] [reason-id]", + Args: cobra.ExactArgs(2), + Short: "Support a standard reporting reason", + Long: "Add the support for the specific standard reporting reason inside the subspace", + Example: fmt.Sprintf(`%s tx reports support-standard-reason 1 1 --from alice`, version.AppName), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } + + subspaceID, err := subspacestypes.ParseSubspaceID(args[0]) + if err != nil { + return err + } + + reasonID, err := types.ParseReasonID(args[1]) + if err != nil { + return err + } + + signer := clientCtx.FromAddress.String() + + msg := types.NewMsgSupportStandardReason(subspaceID, reasonID, signer) + if err = msg.ValidateBasic(); err != nil { + return fmt.Errorf("message validation failed: %w", err) + } + + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + }, + } + + flags.AddTxFlagsToCmd(cmd) + + return cmd +} + +// GetCmdAddReason returns the command allowing to add a new reporting reason +func GetCmdAddReason() *cobra.Command { + cmd := &cobra.Command{ + Use: "add-reason [subspace-id] [title] [[description]]", + Args: cobra.RangeArgs(2, 3), + Short: "Add a new reporting reason", + Long: "Add a new reporting reason with the given title and optional description to a subspace", + Example: fmt.Sprintf(` +%s tx reports add-reason "Spam" "Spam content or spammer user" \ + --from alice +`, version.AppName), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } + + subspaceID, err := subspacestypes.ParseSubspaceID(args[0]) + if err != nil { + return err + } + + title := args[1] + + var description string + if len(args) > 2 { + description = args[2] + } + + signer := clientCtx.FromAddress.String() + + msg := types.NewMsgAddReason(subspaceID, title, description, signer) + if err = msg.ValidateBasic(); err != nil { + return fmt.Errorf("message validation failed: %w", err) + } + + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + }, + } + + flags.AddTxFlagsToCmd(cmd) + + return cmd +} + +// GetCmdRemoveReason returns the command allowing to remove a reporting reason +func GetCmdRemoveReason() *cobra.Command { + cmd := &cobra.Command{ + Use: "remove-reason [subspace-id] [reason-id]", + Args: cobra.ExactArgs(2), + Short: "Remove a reporting reason", + Long: "Remove the reporting reason having the given id from the specified subspace", + Example: fmt.Sprintf(`%s tx reports remove-reason 1 1 --from alice`, version.AppName), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } + + subspaceID, err := subspacestypes.ParseSubspaceID(args[0]) + if err != nil { + return err + } + + reasonID, err := types.ParseReasonID(args[1]) + if err != nil { + return err + } + + signer := clientCtx.FromAddress.String() + + msg := types.NewMsgRemoveReason(subspaceID, reasonID, signer) + if err = msg.ValidateBasic(); err != nil { + return fmt.Errorf("message validation failed: %w", err) + } + + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + }, + } + + flags.AddTxFlagsToCmd(cmd) + + return cmd +} diff --git a/x/reports/keeper/alias_functions.go b/x/reports/keeper/alias_functions.go new file mode 100644 index 0000000000..5b123b39c3 --- /dev/null +++ b/x/reports/keeper/alias_functions.go @@ -0,0 +1,164 @@ +package keeper + +import ( + "fmt" + + poststypes "github.com/desmos-labs/desmos/v3/x/posts/types" + + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/desmos-labs/desmos/v3/x/reports/types" + + subspacestypes "github.com/desmos-labs/desmos/v3/x/subspaces/types" +) + +// HasSubspace tells whether the subspace with the given id exists or not +func (k Keeper) HasSubspace(ctx sdk.Context, subspaceID uint64) bool { + return k.sk.HasSubspace(ctx, subspaceID) +} + +// HasPermission tells whether the given user has the provided permission inside the subspace with the specified id +func (k Keeper) HasPermission(ctx sdk.Context, subspaceID uint64, user string, permission subspacestypes.Permission) bool { + // Report-related permissions are checked only against the root section + return k.sk.HasPermission(ctx, subspaceID, subspacestypes.RootSectionID, user, permission) +} + +// HasUserBlocked tells whether the given blocker has blocked the user inside the provided subspace +func (k Keeper) HasUserBlocked(ctx sdk.Context, blocker, user string, subspaceID uint64) bool { + return k.rk.HasUserBlocked(ctx, blocker, user, subspaceID) +} + +// HasPost tells whether the given post exists or not +func (k Keeper) HasPost(ctx sdk.Context, subspaceID uint64, postID uint64) bool { + return k.pk.HasPost(ctx, subspaceID, postID) +} + +// GetPost returns the post associated with the given id +func (k Keeper) GetPost(ctx sdk.Context, subspaceID uint64, postID uint64) (poststypes.Post, bool) { + return k.pk.GetPost(ctx, subspaceID, postID) +} + +// -------------------------------------------------------------------------------------------------------------------- + +// GetStandardReason returns the standard reason with the given id. +// If no standard reason with the given id could be found, the method will return an empty standard reason and false +func (k Keeper) GetStandardReason(ctx sdk.Context, id uint32) (reason types.StandardReason, found bool) { + for _, reason := range k.GetParams(ctx).StandardReasons { + if reason.ID == id { + return reason, true + } + } + return types.StandardReason{}, false +} + +// -------------------------------------------------------------------------------------------------------------------- + +// IterateReasons iterates over all the stored reasons and performs the provided function +func (k Keeper) IterateReasons(ctx sdk.Context, fn func(reason types.Reason) (stop bool)) { + store := ctx.KVStore(k.storeKey) + iterator := sdk.KVStorePrefixIterator(store, types.ReasonPrefix) + defer iterator.Close() + + for ; iterator.Valid(); iterator.Next() { + var reason types.Reason + k.cdc.MustUnmarshal(iterator.Value(), &reason) + + stop := fn(reason) + if stop { + break + } + } +} + +// IterateSubspaceReasons iterates over all the given subspace reasons and performs the provided function +func (k Keeper) IterateSubspaceReasons(ctx sdk.Context, subspaceID uint64, fn func(reason types.Reason) (stop bool)) { + store := ctx.KVStore(k.storeKey) + iterator := sdk.KVStorePrefixIterator(store, types.SubspaceReasonsPrefix(subspaceID)) + defer iterator.Close() + + for ; iterator.Valid(); iterator.Next() { + var reason types.Reason + k.cdc.MustUnmarshal(iterator.Value(), &reason) + + stop := fn(reason) + if stop { + break + } + } +} + +// GetSubspaceReasons returns the reporting reasons for the given subspace +func (k Keeper) GetSubspaceReasons(ctx sdk.Context, subspaceID uint64) []types.Reason { + var reasons []types.Reason + k.IterateSubspaceReasons(ctx, subspaceID, func(reason types.Reason) (stop bool) { + reasons = append(reasons, reason) + return false + }) + return reasons +} + +// -------------------------------------------------------------------------------------------------------------------- + +// IterateReports iterates over all reports and performs the provided function +func (k Keeper) IterateReports(ctx sdk.Context, fn func(report types.Report) (stop bool)) { + store := ctx.KVStore(k.storeKey) + iterator := sdk.KVStorePrefixIterator(store, types.ReportPrefix) + defer iterator.Close() + + for ; iterator.Valid(); iterator.Next() { + var report types.Report + k.cdc.MustUnmarshal(iterator.Value(), &report) + + stop := fn(report) + if stop { + break + } + } +} + +// IterateSubspaceReports iterates over all the given subspace reports and performs the provided function +func (k Keeper) IterateSubspaceReports(ctx sdk.Context, subspaceID uint64, fn func(report types.Report) (stop bool)) { + store := ctx.KVStore(k.storeKey) + iterator := sdk.KVStorePrefixIterator(store, types.SubspaceReportsPrefix(subspaceID)) + defer iterator.Close() + + for ; iterator.Valid(); iterator.Next() { + var report types.Report + k.cdc.MustUnmarshal(iterator.Value(), &report) + + stop := fn(report) + if stop { + break + } + } +} + +// GetSubspaceReports returns all the reports for the given subspace +func (k Keeper) GetSubspaceReports(ctx sdk.Context, subspaceID uint64) []types.Report { + var reports []types.Report + k.IterateSubspaceReports(ctx, subspaceID, func(report types.Report) (stop bool) { + reports = append(reports, report) + return false + }) + return reports +} + +// IteratePostReports iterates over all the reports for the given post and performs the provided function +func (k Keeper) IteratePostReports(ctx sdk.Context, subspaceID uint64, postID uint64, fn func(report types.Report) (stop bool)) { + store := ctx.KVStore(k.storeKey) + iterator := sdk.KVStorePrefixIterator(store, types.PostReportsPrefix(subspaceID, postID)) + defer iterator.Close() + + for ; iterator.Valid(); iterator.Next() { + reportID := types.GetReportIDFromBytes(iterator.Value()) + report, found := k.GetReport(ctx, subspaceID, reportID) + if !found { + panic(fmt.Errorf("report not found: subspace id %d, report id %d", subspaceID, reportID)) + } + + stop := fn(report) + if stop { + break + } + } +} diff --git a/x/reports/keeper/common_test.go b/x/reports/keeper/common_test.go new file mode 100644 index 0000000000..34c1fb2a9c --- /dev/null +++ b/x/reports/keeper/common_test.go @@ -0,0 +1,84 @@ +package keeper_test + +import ( + "testing" + + postskeeper "github.com/desmos-labs/desmos/v3/x/posts/keeper" + poststypes "github.com/desmos-labs/desmos/v3/x/posts/types" + + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/store" + sdk "github.com/cosmos/cosmos-sdk/types" + paramskeeper "github.com/cosmos/cosmos-sdk/x/params/keeper" + paramstypes "github.com/cosmos/cosmos-sdk/x/params/types" + "github.com/stretchr/testify/suite" + "github.com/tendermint/tendermint/libs/log" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + db "github.com/tendermint/tm-db" + + "github.com/desmos-labs/desmos/v3/app" + relationshipskeeper "github.com/desmos-labs/desmos/v3/x/relationships/keeper" + relationshipstypes "github.com/desmos-labs/desmos/v3/x/relationships/types" + "github.com/desmos-labs/desmos/v3/x/reports/keeper" + "github.com/desmos-labs/desmos/v3/x/reports/types" + subspaceskeeper "github.com/desmos-labs/desmos/v3/x/subspaces/keeper" + subspacestypes "github.com/desmos-labs/desmos/v3/x/subspaces/types" +) + +type KeeperTestsuite struct { + suite.Suite + + cdc codec.Codec + legacyAminoCdc *codec.LegacyAmino + ctx sdk.Context + + storeKey sdk.StoreKey + k keeper.Keeper + + sk subspaceskeeper.Keeper + rk relationshipskeeper.Keeper + pk postskeeper.Keeper +} + +func (suite *KeeperTestsuite) SetupTest() { + // Define store keys + keys := sdk.NewMemoryStoreKeys(types.StoreKey, poststypes.StoreKey, relationshipstypes.StoreKey, subspacestypes.StoreKey, paramstypes.StoreKey) + tKeys := sdk.NewTransientStoreKeys(paramstypes.TStoreKey) + suite.storeKey = keys[types.StoreKey] + + // Create an in-memory db + memDB := db.NewMemDB() + ms := store.NewCommitMultiStore(memDB) + for _, key := range keys { + ms.MountStoreWithDB(key, sdk.StoreTypeIAVL, memDB) + } + for _, tKey := range tKeys { + ms.MountStoreWithDB(tKey, sdk.StoreTypeTransient, memDB) + } + + if err := ms.LoadLatestVersion(); err != nil { + panic(err) + } + + suite.ctx = sdk.NewContext(ms, tmproto.Header{ChainID: "test-chain"}, false, log.NewNopLogger()) + suite.cdc, suite.legacyAminoCdc = app.MakeCodecs() + + paramsKeeper := paramskeeper.NewKeeper(suite.cdc, suite.legacyAminoCdc, keys[paramstypes.StoreKey], tKeys[paramstypes.TStoreKey]) + + // Define keeper + suite.sk = subspaceskeeper.NewKeeper(suite.cdc, keys[subspacestypes.StoreKey]) + suite.rk = relationshipskeeper.NewKeeper(suite.cdc, keys[relationshipstypes.StoreKey], suite.sk) + suite.pk = postskeeper.NewKeeper(suite.cdc, keys[poststypes.StoreKey], paramsKeeper.Subspace(poststypes.DefaultParamsSpace), suite.sk, suite.rk) + suite.k = keeper.NewKeeper( + suite.cdc, + suite.storeKey, + paramsKeeper.Subspace(types.DefaultParamsSpace), + suite.sk, + suite.rk, + suite.pk, + ) +} + +func TestKeeperTestSuite(t *testing.T) { + suite.Run(t, new(KeeperTestsuite)) +} diff --git a/x/reports/keeper/external_hooks.go b/x/reports/keeper/external_hooks.go new file mode 100644 index 0000000000..49d6f1919f --- /dev/null +++ b/x/reports/keeper/external_hooks.go @@ -0,0 +1,105 @@ +package keeper + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + + poststypes "github.com/desmos-labs/desmos/v3/x/posts/types" + + "github.com/desmos-labs/desmos/v3/x/reports/types" + subspacestypes "github.com/desmos-labs/desmos/v3/x/subspaces/types" +) + +type Hooks struct { + k Keeper +} + +var ( + _ subspacestypes.SubspacesHooks = Hooks{} + _ poststypes.PostsHooks = Hooks{} +) + +// Hooks creates a new reports hooks +func (k Keeper) Hooks() Hooks { return Hooks{k} } + +// AfterSubspaceSaved implements subspacestypes.Hooks +func (h Hooks) AfterSubspaceSaved(ctx sdk.Context, subspaceID uint64) { + // Create the initial reason and report id + h.k.SetNextReasonID(ctx, subspaceID, 1) + h.k.SetNextReportID(ctx, subspaceID, 1) +} + +// AfterSubspaceDeleted implements subspacestypes.Hooks +func (h Hooks) AfterSubspaceDeleted(ctx sdk.Context, subspaceID uint64) { + // Delete the reason id key + h.k.DeleteNextReasonID(ctx, subspaceID) + + // Delete all the reasons related to this subspace + h.k.IterateSubspaceReasons(ctx, subspaceID, func(reason types.Reason) (stop bool) { + h.k.DeleteReason(ctx, reason.SubspaceID, reason.ID) + return false + }) + + // Delete the report id key + h.k.DeleteNextReportID(ctx, subspaceID) + + // Delete all the reports related to this subspace + h.k.IterateSubspaceReports(ctx, subspaceID, func(report types.Report) (stop bool) { + h.k.DeleteReport(ctx, report.SubspaceID, report.ID) + return false + }) +} + +// AfterSubspaceGroupSaved implements subspacestypes.Hooks +func (h Hooks) AfterSubspaceGroupSaved(sdk.Context, uint64, uint32) {} + +// AfterSubspaceGroupDeleted implements subspacestypes.Hooks +func (h Hooks) AfterSubspaceGroupDeleted(sdk.Context, uint64, uint32) {} + +// AfterSubspaceSectionSaved implements subspacestypes.Hooks +func (h Hooks) AfterSubspaceSectionSaved(sdk.Context, uint64, uint32) {} + +// AfterSubspaceSectionDeleted implements subspacestypes.Hooks +func (h Hooks) AfterSubspaceSectionDeleted(sdk.Context, uint64, uint32) {} + +// AfterSubspaceGroupMemberAdded implements subspacestypes.Hooks +func (h Hooks) AfterSubspaceGroupMemberAdded(sdk.Context, uint64, uint32, string) { +} + +// AfterSubspaceGroupMemberRemoved implements subspacestypes.Hooks +func (h Hooks) AfterSubspaceGroupMemberRemoved(sdk.Context, uint64, uint32, string) { +} + +// AfterUserPermissionSet implements subspacestypes.Hooks +func (h Hooks) AfterUserPermissionSet(sdk.Context, uint64, uint32, string, subspacestypes.Permission) { +} + +// AfterUserPermissionRemoved implements subspacestypes.Hooks +func (h Hooks) AfterUserPermissionRemoved(sdk.Context, uint64, uint32, string) { +} + +// AfterPostSaved implements poststypes.PostsHooks +func (h Hooks) AfterPostSaved(sdk.Context, uint64, uint64) {} + +// AfterPostDeleted implements poststypes.PostsHooks +func (h Hooks) AfterPostDeleted(ctx sdk.Context, subspaceID uint64, postID uint64) { + // Delete all the reports related to this post + h.k.IteratePostReports(ctx, subspaceID, postID, func(report types.Report) (stop bool) { + h.k.DeleteReport(ctx, report.SubspaceID, report.ID) + return false + }) +} + +// AfterAttachmentSaved implements poststypes.PostsHooks +func (h Hooks) AfterAttachmentSaved(sdk.Context, uint64, uint64, uint32) {} + +// AfterAttachmentDeleted implements poststypes.PostsHooks +func (h Hooks) AfterAttachmentDeleted(sdk.Context, uint64, uint64, uint32) {} + +// AfterPollAnswerSaved implements poststypes.PostsHooks +func (h Hooks) AfterPollAnswerSaved(sdk.Context, uint64, uint64, uint32, string) {} + +// AfterPollAnswerDeleted implements poststypes.PostsHooks +func (h Hooks) AfterPollAnswerDeleted(sdk.Context, uint64, uint64, uint32, string) {} + +// AfterPollVotingPeriodEnded implements poststypes.PostsHooks +func (h Hooks) AfterPollVotingPeriodEnded(sdk.Context, uint64, uint64, uint32) {} diff --git a/x/reports/keeper/external_hooks_test.go b/x/reports/keeper/external_hooks_test.go new file mode 100644 index 0000000000..ef07ec031b --- /dev/null +++ b/x/reports/keeper/external_hooks_test.go @@ -0,0 +1,190 @@ +package keeper_test + +import ( + "time" + + poststypes "github.com/desmos-labs/desmos/v3/x/posts/types" + + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/desmos-labs/desmos/v3/x/reports/types" + subspacestypes "github.com/desmos-labs/desmos/v3/x/subspaces/types" +) + +func (suite *KeeperTestsuite) TestKeeper_AfterSubspaceSaved() { + testCases := []struct { + name string + store func(ctx sdk.Context) + subspace subspacestypes.Subspace + check func(ctx sdk.Context) + }{ + { + name: "saving a subspaces adds the correct keys", + subspace: subspacestypes.NewSubspace( + 1, + "Test subspace", + "This is a test subspace", + "cosmos1a0cj0j6ujn2xap8p40y6648d0w2npytw3xvenm", + "cosmos1a0cj0j6ujn2xap8p40y6648d0w2npytw3xvenm", + "cosmos1a0cj0j6ujn2xap8p40y6648d0w2npytw3xvenm", + time.Date(2020, 1, 2, 12, 00, 00, 000, time.UTC), + ), + check: func(ctx sdk.Context) { + storedReasonID, err := suite.k.GetNextReasonID(ctx, 1) + suite.Require().NoError(err) + suite.Require().Equal(uint32(1), storedReasonID) + + storedReportID, err := suite.k.GetNextReportID(ctx, 1) + suite.Require().NoError(err) + suite.Require().Equal(uint64(1), storedReportID) + }, + }, + } + + // Set the hooks + suite.sk.SetHooks(suite.k.Hooks()) + + for _, tc := range testCases { + tc := tc + suite.Run(tc.name, func() { + ctx, _ := suite.ctx.CacheContext() + if tc.store != nil { + tc.store(ctx) + } + + suite.sk.SaveSubspace(ctx, tc.subspace) + if tc.check != nil { + tc.check(ctx) + } + }) + } +} + +func (suite *KeeperTestsuite) TestKeeper_AfterSubspaceDeleted() { + testCases := []struct { + name string + store func(ctx sdk.Context) + subspaceID uint64 + check func(ctx sdk.Context) + }{ + { + name: "deleting a subspace removes all the reasons and reports", + store: func(ctx sdk.Context) { + suite.k.SetNextReasonID(ctx, 1, 2) + suite.k.SaveReason(ctx, types.NewReason( + 1, + 1, + "Spam", + "This content is spam", + )) + + suite.k.SetNextReportID(ctx, 1, 2) + suite.k.SaveReport(ctx, types.NewReport( + 1, + 1, + []uint32{1}, + "", + types.NewPostTarget(1), + "cosmos1atdl3cpms89md5qa3rxtql0drtgftch2zgkr7v", + time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC), + )) + }, + subspaceID: 1, + check: func(ctx sdk.Context) { + store := ctx.KVStore(suite.storeKey) + + // Check the reasons data + suite.Require().False(store.Has(types.NextReasonIDStoreKey(1))) + suite.Require().False(store.Has(types.ReasonStoreKey(1, 1))) + + // Check the reports data + suite.Require().False(store.Has(types.NextReportIDStoreKey(1))) + suite.Require().False(store.Has(types.ReportStoreKey(1, 1))) + }, + }, + } + + // Set the hooks + suite.sk.SetHooks(suite.k.Hooks()) + + for _, tc := range testCases { + tc := tc + suite.Run(tc.name, func() { + ctx, _ := suite.ctx.CacheContext() + if tc.store != nil { + tc.store(ctx) + } + + suite.sk.DeleteSubspace(ctx, tc.subspaceID) + if tc.check != nil { + tc.check(ctx) + } + }) + } +} + +func (suite *KeeperTestsuite) TestKeeper_AfterPostDeleted() { + testCases := []struct { + name string + store func(ctx sdk.Context) + subspaceID uint64 + postID uint64 + check func(ctx sdk.Context) + }{ + { + name: "deleting a post removes all the associated reports", + store: func(ctx sdk.Context) { + suite.pk.SavePost(ctx, poststypes.NewPost( + 1, + 0, + 1, + "External ID", + "This is a text", + "cosmos13t6y2nnugtshwuy0zkrq287a95lyy8vzleaxmd", + 1, + nil, + nil, + poststypes.REPLY_SETTING_EVERYONE, + time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC), + nil, + )) + + suite.k.SaveReport(ctx, types.NewReport( + 1, + 1, + []uint32{1}, + "", + types.NewPostTarget(1), + "cosmos1atdl3cpms89md5qa3rxtql0drtgftch2zgkr7v", + time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC), + )) + }, + subspaceID: 1, + postID: 1, + check: func(ctx sdk.Context) { + store := ctx.KVStore(suite.storeKey) + + // Make sure the report does not exist + suite.Require().False(store.Has(types.ReportStoreKey(1, 1))) + }, + }, + } + + // Set the hooks + suite.pk.SetHooks(suite.k.Hooks()) + + for _, tc := range testCases { + tc := tc + suite.Run(tc.name, func() { + ctx, _ := suite.ctx.CacheContext() + if tc.store != nil { + tc.store(ctx) + } + + suite.pk.DeletePost(ctx, tc.subspaceID, tc.postID) + if tc.check != nil { + tc.check(ctx) + } + }) + } +} diff --git a/x/reports/keeper/genesis.go b/x/reports/keeper/genesis.go new file mode 100644 index 0000000000..3adb015db8 --- /dev/null +++ b/x/reports/keeper/genesis.go @@ -0,0 +1,87 @@ +package keeper + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/desmos-labs/desmos/v3/x/reports/types" + subspacestypes "github.com/desmos-labs/desmos/v3/x/subspaces/types" +) + +// ExportGenesis returns the GenesisState associated with the given context +func (k Keeper) ExportGenesis(ctx sdk.Context) *types.GenesisState { + return types.NewGenesisState( + k.getSubspaceDataEntries(ctx), + k.getAllReasons(ctx), + k.getAllReports(ctx), + k.GetParams(ctx), + ) +} + +// getSubspaceDataEntries returns the subspaces data entries stored in the given context +func (k Keeper) getSubspaceDataEntries(ctx sdk.Context) []types.SubspaceDataEntry { + var entries []types.SubspaceDataEntry + k.sk.IterateSubspaces(ctx, func(subspace subspacestypes.Subspace) (stop bool) { + nextReasonID, err := k.GetNextReasonID(ctx, subspace.ID) + if err != nil { + panic(err) + } + + nextReportID, err := k.GetNextReportID(ctx, subspace.ID) + if err != nil { + panic(err) + } + + entries = append(entries, types.NewSubspacesDataEntry( + subspace.ID, + nextReasonID, + nextReportID, + )) + + return false + }) + return entries +} + +// getAllReasons returns all the reasons stored inside the given context +func (k Keeper) getAllReasons(ctx sdk.Context) []types.Reason { + var reasons []types.Reason + k.IterateReasons(ctx, func(reason types.Reason) (stop bool) { + reasons = append(reasons, reason) + return false + }) + return reasons +} + +// getAllReports returns all the reports stored inside the given context +func (k Keeper) getAllReports(ctx sdk.Context) []types.Report { + var reports []types.Report + k.IterateReports(ctx, func(report types.Report) (stop bool) { + reports = append(reports, report) + return false + }) + return reports +} + +// -------------------------------------------------------------------------------------------------------------------- + +// InitGenesis initializes the chain state based on the given GenesisState +func (k Keeper) InitGenesis(ctx sdk.Context, data types.GenesisState) { + // Initialize the subspaces data + for _, entry := range data.SubspacesData { + k.SetNextReasonID(ctx, entry.SubspaceID, entry.ReasonID) + k.SetNextReportID(ctx, entry.SubspaceID, entry.ReportID) + } + + // Initialize all the reasons + for _, reason := range data.Reasons { + k.SaveReason(ctx, reason) + } + + // Initialize all the reports + for _, report := range data.Reports { + k.SaveReport(ctx, report) + } + + // Initialize the params + k.SetParams(ctx, data.Params) +} diff --git a/x/reports/keeper/genesis_test.go b/x/reports/keeper/genesis_test.go new file mode 100644 index 0000000000..7d0df8064c --- /dev/null +++ b/x/reports/keeper/genesis_test.go @@ -0,0 +1,233 @@ +package keeper_test + +import ( + "time" + + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/desmos-labs/desmos/v3/x/reports/types" + subspacestypes "github.com/desmos-labs/desmos/v3/x/subspaces/types" +) + +func (suite *KeeperTestsuite) TestKeeper_ExportGenesis() { + testCases := []struct { + name string + store func(ctx sdk.Context) + expGenesis *types.GenesisState + }{ + { + name: "subspaces data entries are exported properly", + store: func(ctx sdk.Context) { + suite.sk.SaveSubspace(ctx, subspacestypes.NewSubspace( + 1, + "Test subspace", + "This is a test subspace", + "cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69", + "cosmos1m0czrla04f7rp3zg7dsgc4kla54q7pc4xt00l5", + "cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69", + time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC), + )) + suite.k.SetNextReasonID(ctx, 1, 1) + suite.k.SetNextReportID(ctx, 1, 2) + + suite.sk.SaveSubspace(ctx, subspacestypes.NewSubspace( + 2, + "Another test subspace", + "This is another test subspace", + "cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69", + "cosmos1m0czrla04f7rp3zg7dsgc4kla54q7pc4xt00l5", + "cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69", + time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC), + )) + suite.k.SetNextReasonID(ctx, 2, 3) + suite.k.SetNextReportID(ctx, 2, 4) + + suite.k.SetParams(ctx, types.Params{}) + }, + expGenesis: types.NewGenesisState([]types.SubspaceDataEntry{ + types.NewSubspacesDataEntry(1, 1, 2), + types.NewSubspacesDataEntry(2, 3, 4), + }, nil, nil, types.Params{}), + }, + { + name: "reasons are exported properly", + store: func(ctx sdk.Context) { + suite.k.SaveReason(ctx, types.NewReason( + 1, + 1, + "Spam", + "This content is spam", + )) + + suite.k.SetParams(ctx, types.Params{}) + }, + expGenesis: types.NewGenesisState(nil, []types.Reason{ + types.NewReason( + 1, + 1, + "Spam", + "This content is spam", + ), + }, nil, types.Params{}), + }, + { + name: "reports are exported properly", + store: func(ctx sdk.Context) { + suite.k.SaveReport(ctx, types.NewReport( + 1, + 1, + []uint32{1}, + "This content is spam", + types.NewUserTarget("cosmos1pjffdtweghpyxru9alssyqtdkq8mn6sepgstgm"), + "cosmos1zkmf50jq4lzvhvp5ekl0sdf2p4g3v9v8edt24z", + time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC), + )) + + suite.k.SetParams(ctx, types.Params{}) + }, + expGenesis: types.NewGenesisState(nil, nil, []types.Report{ + types.NewReport( + 1, + 1, + []uint32{1}, + "This content is spam", + types.NewUserTarget("cosmos1pjffdtweghpyxru9alssyqtdkq8mn6sepgstgm"), + "cosmos1zkmf50jq4lzvhvp5ekl0sdf2p4g3v9v8edt24z", + time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC), + ), + }, types.Params{}), + }, + { + name: "params are exported properly", + store: func(ctx sdk.Context) { + suite.k.SetParams(ctx, types.NewParams([]types.StandardReason{ + types.NewStandardReason(1, "Spam", "This content is spam"), + })) + }, + expGenesis: types.NewGenesisState(nil, nil, nil, types.NewParams([]types.StandardReason{ + types.NewStandardReason(1, "Spam", "This content is spam"), + })), + }, + } + + for _, tc := range testCases { + tc := tc + suite.Run(tc.name, func() { + ctx, _ := suite.ctx.CacheContext() + if tc.store != nil { + tc.store(ctx) + } + + genesis := suite.k.ExportGenesis(ctx) + suite.Require().Equal(tc.expGenesis, genesis) + }) + } +} + +func (suite *KeeperTestsuite) TestKeeper_InitGenesis() { + testCases := []struct { + name string + store func(ctx sdk.Context) + data types.GenesisState + check func(ctx sdk.Context) + }{ + { + name: "subspaces data are initialized properly", + data: types.GenesisState{ + SubspacesData: []types.SubspaceDataEntry{ + types.NewSubspacesDataEntry(1, 2, 3), + }, + }, + check: func(ctx sdk.Context) { + nextReasonID, err := suite.k.GetNextReasonID(ctx, 1) + suite.Require().NoError(err) + suite.Require().Equal(uint32(2), nextReasonID) + + nextReportID, err := suite.k.GetNextReportID(ctx, 1) + suite.Require().NoError(err) + suite.Require().Equal(uint64(3), nextReportID) + }, + }, + { + name: "reasons are imported properly", + data: types.GenesisState{ + Reasons: []types.Reason{ + types.NewReason( + 2, + 1, + "Spam", + "This content is spam", + ), + }, + }, + check: func(ctx sdk.Context) { + stored, found := suite.k.GetReason(ctx, 2, 1) + suite.Require().True(found) + suite.Require().Equal(types.NewReason( + 2, + 1, + "Spam", + "This content is spam", + ), stored) + }, + }, + { + name: "reports are imported properly", + data: types.GenesisState{ + Reports: []types.Report{ + types.NewReport( + 1, + 1, + []uint32{1}, + "This content is spam", + types.NewPostTarget(1), + "cosmos1zkmf50jq4lzvhvp5ekl0sdf2p4g3v9v8edt24z", + time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC), + ), + }, + }, + check: func(ctx sdk.Context) { + stored, found := suite.k.GetReport(ctx, 1, 1) + suite.Require().True(found) + suite.Require().Equal(types.NewReport( + 1, + 1, + []uint32{1}, + "This content is spam", + types.NewPostTarget(1), + "cosmos1zkmf50jq4lzvhvp5ekl0sdf2p4g3v9v8edt24z", + time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC), + ), stored) + }, + }, + { + name: "params are initialized properly", + data: types.GenesisState{ + Params: types.NewParams([]types.StandardReason{ + types.NewStandardReason(1, "Spam", "This content is spam"), + }), + }, + check: func(ctx sdk.Context) { + stored := suite.k.GetParams(ctx) + suite.Require().Equal(types.NewParams([]types.StandardReason{ + types.NewStandardReason(1, "Spam", "This content is spam"), + }), stored) + }, + }, + } + + for _, tc := range testCases { + tc := tc + suite.Run(tc.name, func() { + ctx, _ := suite.ctx.CacheContext() + if tc.store != nil { + tc.store(ctx) + } + + suite.k.InitGenesis(ctx, tc.data) + if tc.check != nil { + tc.check(ctx) + } + }) + } +} diff --git a/x/reports/keeper/grpc_query.go b/x/reports/keeper/grpc_query.go new file mode 100644 index 0000000000..87ccad96a4 --- /dev/null +++ b/x/reports/keeper/grpc_query.go @@ -0,0 +1,136 @@ +package keeper + +import ( + "bytes" + "context" + "fmt" + + "github.com/cosmos/cosmos-sdk/store/prefix" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/cosmos/cosmos-sdk/types/query" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + + "github.com/desmos-labs/desmos/v3/x/reports/types" +) + +var _ types.QueryServer = &Keeper{} + +// getReportsStorePrefix returns the store prefix to be used to iterate reports based on the given request data +func getReportsStorePrefix(request *types.QueryReportsRequest) ([]byte, error) { + if request.Target == nil { + return types.SubspaceReportsPrefix(request.SubspaceId), nil + } + + switch target := request.Target.GetCachedValue().(type) { + case *types.UserTarget: + if request.Reporter != "" { + return types.UserReportStoreKey(request.SubspaceId, target.User, request.Reporter), nil + } + return types.UserReportsPrefix(request.SubspaceId, target.User), nil + + case *types.PostTarget: + if request.Reporter != "" { + return types.PostReportStoreKey(request.SubspaceId, target.PostID, request.Reporter), nil + } + return types.PostReportsPrefix(request.SubspaceId, target.PostID), nil + + default: + return nil, fmt.Errorf("invalid target type") + } +} + +// Reports implements the QueryReports gRPC method +func (k Keeper) Reports(ctx context.Context, request *types.QueryReportsRequest) (*types.QueryReportsResponse, error) { + if request.SubspaceId == 0 { + return nil, sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "invalid subspace id") + } + + sdkCtx := sdk.UnwrapSDKContext(ctx) + store := sdkCtx.KVStore(k.storeKey) + + // Get the proper store prefix + storePrefix, err := getReportsStorePrefix(request) + if err != nil { + return nil, status.Error(codes.Internal, err.Error()) + } + reportsStore := prefix.NewStore(store, storePrefix) + + var reports []types.Report + pageRes, err := query.Paginate(reportsStore, request.Pagination, func(key []byte, value []byte) error { + var report types.Report + + switch { + case bytes.HasPrefix(storePrefix, types.ReportPrefix): + err = k.cdc.Unmarshal(value, &report) + + case bytes.HasPrefix(storePrefix, types.UsersReportsPrefix), + bytes.HasPrefix(storePrefix, types.PostsReportsPrefix): + reportID := types.GetReportIDFromBytes(value) + storedReport, found := k.GetReport(sdkCtx, request.SubspaceId, reportID) + if !found { + err = fmt.Errorf("report reference not found") + } + report = storedReport + + } + + if err != nil { + return err + } + + reports = append(reports, report) + return nil + }) + + if err != nil { + return nil, status.Error(codes.Internal, err.Error()) + } + + return &types.QueryReportsResponse{ + Reports: reports, + Pagination: pageRes, + }, nil +} + +// Reasons implements the QueryReasons gRPC method +func (k Keeper) Reasons(ctx context.Context, request *types.QueryReasonsRequest) (*types.QueryReasonsResponse, error) { + if request.SubspaceId == 0 { + return nil, sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "invalid subspace id") + } + + sdkCtx := sdk.UnwrapSDKContext(ctx) + store := sdkCtx.KVStore(k.storeKey) + reasonsStore := prefix.NewStore(store, types.SubspaceReasonsPrefix(request.SubspaceId)) + + var reasons []types.Reason + pageRes, err := query.Paginate(reasonsStore, request.Pagination, func(key []byte, value []byte) error { + var reason types.Reason + if err := k.cdc.Unmarshal(value, &reason); err != nil { + return status.Error(codes.Internal, err.Error()) + } + + reasons = append(reasons, reason) + return nil + }) + + if err != nil { + return nil, status.Error(codes.Internal, err.Error()) + } + + return &types.QueryReasonsResponse{ + Reasons: reasons, + Pagination: pageRes, + }, nil +} + +// Params implements the QueryParams gRPC method +func (k Keeper) Params(ctx context.Context, _ *types.QueryParamsRequest) (*types.QueryParamsResponse, error) { + sdkCtx := sdk.UnwrapSDKContext(ctx) + params := k.GetParams(sdkCtx) + + return &types.QueryParamsResponse{ + Params: params, + }, nil +} diff --git a/x/reports/keeper/grpc_query_test.go b/x/reports/keeper/grpc_query_test.go new file mode 100644 index 0000000000..13dd616980 --- /dev/null +++ b/x/reports/keeper/grpc_query_test.go @@ -0,0 +1,405 @@ +package keeper_test + +import ( + "time" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/query" + + "github.com/desmos-labs/desmos/v3/x/reports/types" +) + +func (suite *KeeperTestsuite) TestQueryServer_Reports() { + testCases := []struct { + name string + store func(ctx sdk.Context) + request *types.QueryReportsRequest + shouldErr bool + expReports []types.Report + }{ + { + name: "invalid subspace id returns error", + request: types.NewQueryReportsRequest(0, nil, "", nil), + shouldErr: true, + }, + { + name: "user reports request returns correct data - with reporter", + store: func(ctx sdk.Context) { + suite.k.SaveReport(ctx, types.NewReport( + 1, + 1, + []uint32{1}, + "This user is spamming", + types.NewUserTarget("cosmos1z0glns8fv5h0xgghg4nkq0jjy9gp0l682tcf79"), + "cosmos1ggzk8tnte9lmzgpvyzzdtmwmn6rjlct4spmjjd", + time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC), + )) + + suite.k.SaveReport(ctx, types.NewReport( + 1, + 2, + []uint32{1}, + "This post is spam", + types.NewPostTarget(1), + "cosmos1ggzk8tnte9lmzgpvyzzdtmwmn6rjlct4spmjjd", + time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC), + )) + }, + request: types.NewQueryReportsRequest( + 1, + types.NewUserTarget("cosmos1z0glns8fv5h0xgghg4nkq0jjy9gp0l682tcf79"), + "cosmos1ggzk8tnte9lmzgpvyzzdtmwmn6rjlct4spmjjd", + nil, + ), + shouldErr: false, + expReports: []types.Report{ + types.NewReport( + 1, + 1, + []uint32{1}, + "This user is spamming", + types.NewUserTarget("cosmos1z0glns8fv5h0xgghg4nkq0jjy9gp0l682tcf79"), + "cosmos1ggzk8tnte9lmzgpvyzzdtmwmn6rjlct4spmjjd", + time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC), + ), + }, + }, + { + name: "user reports request returns correct data", + store: func(ctx sdk.Context) { + suite.k.SaveReport(ctx, types.NewReport( + 1, + 1, + []uint32{1}, + "This user is spamming", + types.NewUserTarget("cosmos1z0glns8fv5h0xgghg4nkq0jjy9gp0l682tcf79"), + "cosmos1ggzk8tnte9lmzgpvyzzdtmwmn6rjlct4spmjjd", + time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC), + )) + + suite.k.SaveReport(ctx, types.NewReport( + 1, + 2, + []uint32{1}, + "This post is spam", + types.NewPostTarget(1), + "cosmos1ggzk8tnte9lmzgpvyzzdtmwmn6rjlct4spmjjd", + time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC), + )) + }, + request: types.NewQueryReportsRequest( + 1, + types.NewUserTarget("cosmos1z0glns8fv5h0xgghg4nkq0jjy9gp0l682tcf79"), + "", + &query.PageRequest{ + Limit: 1, + }, + ), + shouldErr: false, + expReports: []types.Report{ + types.NewReport( + 1, + 1, + []uint32{1}, + "This user is spamming", + types.NewUserTarget("cosmos1z0glns8fv5h0xgghg4nkq0jjy9gp0l682tcf79"), + "cosmos1ggzk8tnte9lmzgpvyzzdtmwmn6rjlct4spmjjd", + time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC), + ), + }, + }, + { + name: "post reports request returns correct data - with reporter", + store: func(ctx sdk.Context) { + suite.k.SaveReport(ctx, types.NewReport( + 1, + 1, + []uint32{1}, + "This user is spamming", + types.NewUserTarget("cosmos1z0glns8fv5h0xgghg4nkq0jjy9gp0l682tcf79"), + "cosmos1ggzk8tnte9lmzgpvyzzdtmwmn6rjlct4spmjjd", + time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC), + )) + + suite.k.SaveReport(ctx, types.NewReport( + 1, + 2, + []uint32{1}, + "This post is spam", + types.NewPostTarget(1), + "cosmos1ggzk8tnte9lmzgpvyzzdtmwmn6rjlct4spmjjd", + time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC), + )) + }, + request: types.NewQueryReportsRequest( + 1, + types.NewPostTarget(1), + "cosmos1ggzk8tnte9lmzgpvyzzdtmwmn6rjlct4spmjjd", + nil, + ), + shouldErr: false, + expReports: []types.Report{ + types.NewReport( + 1, + 2, + []uint32{1}, + "This post is spam", + types.NewPostTarget(1), + "cosmos1ggzk8tnte9lmzgpvyzzdtmwmn6rjlct4spmjjd", + time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC), + ), + }, + }, + { + name: "post reports request returns correct data", + store: func(ctx sdk.Context) { + suite.k.SaveReport(ctx, types.NewReport( + 1, + 1, + []uint32{1}, + "This user is spamming", + types.NewUserTarget("cosmos1z0glns8fv5h0xgghg4nkq0jjy9gp0l682tcf79"), + "cosmos1ggzk8tnte9lmzgpvyzzdtmwmn6rjlct4spmjjd", + time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC), + )) + + suite.k.SaveReport(ctx, types.NewReport( + 1, + 2, + []uint32{1}, + "This post is spam", + types.NewPostTarget(1), + "cosmos1ggzk8tnte9lmzgpvyzzdtmwmn6rjlct4spmjjd", + time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC), + )) + }, + request: types.NewQueryReportsRequest( + 1, + types.NewPostTarget(1), + "", + &query.PageRequest{ + Limit: 1, + }, + ), + shouldErr: false, + expReports: []types.Report{ + types.NewReport( + 1, + 2, + []uint32{1}, + "This post is spam", + types.NewPostTarget(1), + "cosmos1ggzk8tnte9lmzgpvyzzdtmwmn6rjlct4spmjjd", + time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC), + ), + }, + }, + { + name: "generic reports request returns correct data", + store: func(ctx sdk.Context) { + suite.k.SaveReport(ctx, types.NewReport( + 1, + 1, + []uint32{1}, + "This user is spamming", + types.NewUserTarget("cosmos1z0glns8fv5h0xgghg4nkq0jjy9gp0l682tcf79"), + "cosmos1ggzk8tnte9lmzgpvyzzdtmwmn6rjlct4spmjjd", + time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC), + )) + + suite.k.SaveReport(ctx, types.NewReport( + 1, + 2, + []uint32{1}, + "This post is spam", + types.NewPostTarget(1), + "cosmos1ggzk8tnte9lmzgpvyzzdtmwmn6rjlct4spmjjd", + time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC), + )) + }, + request: types.NewQueryReportsRequest( + 1, + nil, + "", + &query.PageRequest{ + Limit: 1, + }, + ), + shouldErr: false, + expReports: []types.Report{ + types.NewReport( + 1, + 1, + []uint32{1}, + "This user is spamming", + types.NewUserTarget("cosmos1z0glns8fv5h0xgghg4nkq0jjy9gp0l682tcf79"), + "cosmos1ggzk8tnte9lmzgpvyzzdtmwmn6rjlct4spmjjd", + time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC), + ), + }, + }, + } + + for _, tc := range testCases { + tc := tc + suite.Run(tc.name, func() { + ctx, _ := suite.ctx.CacheContext() + if tc.store != nil { + tc.store(ctx) + } + + res, err := suite.k.Reports(sdk.WrapSDKContext(ctx), tc.request) + if tc.shouldErr { + suite.Require().Error(err) + } else { + suite.Require().NoError(err) + suite.Require().Equal(tc.expReports, res.Reports) + } + }) + } +} + +func (suite *KeeperTestsuite) TestQueryServer_Reasons() { + testCases := []struct { + name string + store func(ctx sdk.Context) + request *types.QueryReasonsRequest + shouldErr bool + expReasons []types.Reason + }{ + { + name: "paginated request returns the correct data", + store: func(ctx sdk.Context) { + suite.k.SaveReason(ctx, types.NewReason( + 1, + 1, + "Spam", + "This content is spam", + )) + + suite.k.SaveReason(ctx, types.NewReason( + 1, + 2, + "Self harm", + "This content contains self harm", + )) + }, + request: types.NewQueryReasonsRequest( + 1, + &query.PageRequest{Limit: 1}, + ), + shouldErr: false, + expReasons: []types.Reason{ + types.NewReason( + 1, + 1, + "Spam", + "This content is spam", + ), + }, + }, + { + name: "non paginated request returns the correct data", + store: func(ctx sdk.Context) { + suite.k.SaveReason(ctx, types.NewReason( + 1, + 1, + "Spam", + "This content is spam", + )) + + suite.k.SaveReason(ctx, types.NewReason( + 1, + 2, + "Self harm", + "This content contains self harm", + )) + }, + request: types.NewQueryReasonsRequest(1, nil), + shouldErr: false, + expReasons: []types.Reason{ + types.NewReason( + 1, + 1, + "Spam", + "This content is spam", + ), + types.NewReason( + 1, + 2, + "Self harm", + "This content contains self harm", + ), + }, + }, + } + + for _, tc := range testCases { + tc := tc + suite.Run(tc.name, func() { + ctx, _ := suite.ctx.CacheContext() + if tc.store != nil { + tc.store(ctx) + } + + res, err := suite.k.Reasons(sdk.WrapSDKContext(ctx), tc.request) + if tc.shouldErr { + suite.Require().Error(err) + } else { + suite.Require().NoError(err) + suite.Require().Equal(tc.expReasons, res.Reasons) + } + }) + } +} + +func (suite *KeeperTestsuite) TestQueryServer_Params() { + testCases := []struct { + name string + store func(ctx sdk.Context) + request *types.QueryParamsRequest + shouldErr bool + expParams types.Params + }{ + { + name: "default params are returned properly", + store: func(ctx sdk.Context) { + suite.k.SetParams(ctx, types.DefaultParams()) + }, + request: types.NewQueryParamsRequest(), + shouldErr: false, + expParams: types.DefaultParams(), + }, + { + name: "custom params return error", + store: func(ctx sdk.Context) { + suite.k.SetParams(ctx, types.NewParams([]types.StandardReason{ + types.NewStandardReason(1, "Spam", "This content is spam"), + })) + }, + request: types.NewQueryParamsRequest(), + shouldErr: false, + expParams: types.NewParams([]types.StandardReason{ + types.NewStandardReason(1, "Spam", "This content is spam"), + }), + }, + } + + for _, tc := range testCases { + tc := tc + suite.Run(tc.name, func() { + ctx, _ := suite.ctx.CacheContext() + if tc.store != nil { + tc.store(ctx) + } + + res, err := suite.k.Params(sdk.WrapSDKContext(ctx), tc.request) + if tc.shouldErr { + suite.Require().Error(err) + } else { + suite.Require().NoError(err) + suite.Require().Equal(tc.expParams, res.Params) + } + }) + } +} diff --git a/x/reports/keeper/hooks.go b/x/reports/keeper/hooks.go new file mode 100644 index 0000000000..870fa4c166 --- /dev/null +++ b/x/reports/keeper/hooks.go @@ -0,0 +1,38 @@ +package keeper + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/desmos-labs/desmos/v3/x/reports/types" +) + +// Implement ReportsHooks interface +var _ types.ReportsHooks = Keeper{} + +// AfterReportSaved implements types.ReportsHooks +func (k Keeper) AfterReportSaved(ctx sdk.Context, subspaceID uint64, reportID uint64) { + if k.hooks != nil { + k.hooks.AfterReportSaved(ctx, subspaceID, reportID) + } +} + +// AfterReportDeleted implements types.ReportsHooks +func (k Keeper) AfterReportDeleted(ctx sdk.Context, subspaceID uint64, reportID uint64) { + if k.hooks != nil { + k.hooks.AfterReportDeleted(ctx, subspaceID, reportID) + } +} + +// AfterReasonSaved implements types.ReportsHooks +func (k Keeper) AfterReasonSaved(ctx sdk.Context, subspaceID uint64, reasonID uint32) { + if k.hooks != nil { + k.hooks.AfterReasonSaved(ctx, subspaceID, reasonID) + } +} + +// AfterReasonDeleted implements types.ReportsHooks +func (k Keeper) AfterReasonDeleted(ctx sdk.Context, subspaceID uint64, reasonID uint32) { + if k.hooks != nil { + k.hooks.AfterReasonDeleted(ctx, subspaceID, reasonID) + } +} diff --git a/x/reports/keeper/invariants.go b/x/reports/keeper/invariants.go new file mode 100644 index 0000000000..7de52fa8b1 --- /dev/null +++ b/x/reports/keeper/invariants.go @@ -0,0 +1,170 @@ +package keeper + +import ( + "fmt" + + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/desmos-labs/desmos/v3/x/reports/types" + subspaceskeeper "github.com/desmos-labs/desmos/v3/x/subspaces/keeper" + subspacestypes "github.com/desmos-labs/desmos/v3/x/subspaces/types" +) + +// RegisterInvariants registers all posts invariants +func RegisterInvariants(ir sdk.InvariantRegistry, keeper Keeper) { + ir.RegisterRoute(types.ModuleName, "valid-subspaces", + ValidSubspacesInvariant(keeper)) + ir.RegisterRoute(types.ModuleName, "valid-reasons", + ValidReasonsInvariant(keeper)) + ir.RegisterRoute(types.ModuleName, "valid-reports", + ValidReportsInvariant(keeper)) +} + +// -------------------------------------------------------------------------------------------------------------------- + +// ValidSubspacesInvariant checks that all the subspaces have a valid reason id and report id to them +func ValidSubspacesInvariant(k Keeper) sdk.Invariant { + return func(ctx sdk.Context) (message string, broken bool) { + var invalidSubspaces []subspacestypes.Subspace + k.sk.IterateSubspaces(ctx, func(subspace subspacestypes.Subspace) (stop bool) { + invalid := false + + // Make sure the next reason id exists + if !k.HasNextReasonID(ctx, subspace.ID) { + invalid = true + } + + if !k.HasNextReportID(ctx, subspace.ID) { + invalid = true + } + + if invalid { + invalidSubspaces = append(invalidSubspaces, subspace) + } + + return false + }) + + return sdk.FormatInvariant(types.ModuleName, "invalid subspaces", + fmt.Sprintf("the following subspaces are invalid:\n%s", subspaceskeeper.FormatOutputSubspaces(invalidSubspaces)), + ), invalidSubspaces != nil + } +} + +// -------------------------------------------------------------------------------------------------------------------- + +// ValidReasonsInvariant checks that all the reasons are valid +func ValidReasonsInvariant(k Keeper) sdk.Invariant { + return func(ctx sdk.Context) (string, bool) { + var invalidReasons []types.Reason + k.IterateReasons(ctx, func(reason types.Reason) (stop bool) { + invalid := false + + // Make sure the subspace exists + if !k.HasSubspace(ctx, reason.SubspaceID) { + invalid = true + } + + nextReasonID, err := k.GetNextReasonID(ctx, reason.SubspaceID) + if err != nil { + invalid = true + } + + // Make sure the reason id is always less than the next one + if reason.ID >= nextReasonID { + invalid = true + } + + // Validate the reason + err = reason.Validate() + if err != nil { + invalid = true + } + + if invalid { + invalidReasons = append(invalidReasons, reason) + } + + return false + + }) + + return sdk.FormatInvariant(types.ModuleName, "invalid reasons", + fmt.Sprintf("the following reasons are invalid:\n%s", formatOutputReasons(invalidReasons)), + ), invalidReasons != nil + } +} + +// formatOutputReasons concatenates the given reasons information into a string +func formatOutputReasons(reasons []types.Reason) (output string) { + for _, reason := range reasons { + output += fmt.Sprintf("SuspaceID: %d, ReasonID: %d\n", reason.SubspaceID, reason.ID) + } + return output +} + +// -------------------------------------------------------------------------------------------------------------------- + +// ValidReportsInvariant checks that all the reports are valid +func ValidReportsInvariant(k Keeper) sdk.Invariant { + return func(ctx sdk.Context) (string, bool) { + var invalidReports []types.Report + k.IterateReports(ctx, func(report types.Report) (stop bool) { + invalid := false + + // Make sure the subspace exists + if !k.HasSubspace(ctx, report.SubspaceID) { + invalid = true + } + + // Make sure the reason exists + for _, reasonID := range report.ReasonsIDs { + if !k.HasReason(ctx, report.SubspaceID, reasonID) { + invalid = true + } + } + + nextReportID, err := k.GetNextReportID(ctx, report.SubspaceID) + if err != nil { + invalid = true + } + + // Make sure the report id is always less than the next one + if report.ID >= nextReportID { + invalid = true + } + + if data, ok := report.Target.GetCachedValue().(*types.PostTarget); ok { + // Make sure the reported post exists + if !k.HasPost(ctx, report.SubspaceID, data.PostID) { + invalid = true + } + } + + // Validate the report + err = report.Validate() + if err != nil { + invalid = true + } + + if invalid { + invalidReports = append(invalidReports, report) + } + + return false + + }) + + return sdk.FormatInvariant(types.ModuleName, "invalid reports", + fmt.Sprintf("the following reasons are invalid:\n%s", formatOutputReports(invalidReports)), + ), invalidReports != nil + } +} + +// formatOutputReports concatenates the given reports information into a string +func formatOutputReports(reports []types.Report) (output string) { + for _, report := range reports { + output += fmt.Sprintf("SuspaceID: %d, ReportID: %d\n", report.SubspaceID, report.ID) + } + return output +} diff --git a/x/reports/keeper/invariants_test.go b/x/reports/keeper/invariants_test.go new file mode 100644 index 0000000000..709de770e5 --- /dev/null +++ b/x/reports/keeper/invariants_test.go @@ -0,0 +1,450 @@ +package keeper_test + +import ( + "time" + + poststypes "github.com/desmos-labs/desmos/v3/x/posts/types" + + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/desmos-labs/desmos/v3/x/reports/keeper" + "github.com/desmos-labs/desmos/v3/x/reports/types" + subspacestypes "github.com/desmos-labs/desmos/v3/x/subspaces/types" +) + +func (suite *KeeperTestsuite) TestValidSubspacesInvariant() { + testCases := []struct { + name string + store func(ctx sdk.Context) + expBroken bool + }{ + { + name: "non existing next reason id breaks invariant", + store: func(ctx sdk.Context) { + suite.sk.SaveSubspace(ctx, subspacestypes.NewSubspace( + 1, + "Test subspace", + "This is a test subspace", + "cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69", + "cosmos1m0czrla04f7rp3zg7dsgc4kla54q7pc4xt00l5", + "cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69", + time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC), + )) + }, + expBroken: true, + }, + { + name: "non existing next report id breaks invariant", + store: func(ctx sdk.Context) { + suite.sk.SaveSubspace(ctx, subspacestypes.NewSubspace( + 1, + "Test subspace", + "This is a test subspace", + "cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69", + "cosmos1m0czrla04f7rp3zg7dsgc4kla54q7pc4xt00l5", + "cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69", + time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC), + )) + suite.k.SetNextReasonID(ctx, 1, 1) + + }, + expBroken: true, + }, + { + name: "valid data does not break invariant", + store: func(ctx sdk.Context) { + suite.sk.SaveSubspace(ctx, subspacestypes.NewSubspace( + 1, + "Test subspace", + "This is a test subspace", + "cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69", + "cosmos1m0czrla04f7rp3zg7dsgc4kla54q7pc4xt00l5", + "cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69", + time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC), + )) + suite.k.SetNextReasonID(ctx, 1, 1) + suite.k.SetNextReportID(ctx, 1, 1) + }, + expBroken: false, + }, + } + + for _, tc := range testCases { + tc := tc + suite.Run(tc.name, func() { + ctx, _ := suite.ctx.CacheContext() + if tc.store != nil { + tc.store(ctx) + } + + _, broken := keeper.ValidSubspacesInvariant(suite.k)(ctx) + suite.Require().Equal(tc.expBroken, broken) + }) + } +} + +func (suite *KeeperTestsuite) TestValidReasonsInvariant() { + testCases := []struct { + name string + store func(ctx sdk.Context) + expBroken bool + }{ + { + name: "non existing subspace breaks invariant", + store: func(ctx sdk.Context) { + suite.k.SaveReason(ctx, types.NewReason( + 1, + 1, + "Spam", + "This content is spam", + )) + }, + expBroken: true, + }, + { + name: "non existing next reason id breaks invariant", + store: func(ctx sdk.Context) { + suite.sk.SaveSubspace(ctx, subspacestypes.NewSubspace( + 1, + "Test subspace", + "This is a test subspace", + "cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69", + "cosmos1m0czrla04f7rp3zg7dsgc4kla54q7pc4xt00l5", + "cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69", + time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC), + )) + + suite.k.SaveReason(ctx, types.NewReason( + 1, + 1, + "Spam", + "This content is spam", + )) + }, + expBroken: true, + }, + { + name: "invalid reason id compared to next reason id breaks invariant", + store: func(ctx sdk.Context) { + suite.sk.SaveSubspace(ctx, subspacestypes.NewSubspace( + 1, + "Test subspace", + "This is a test subspace", + "cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69", + "cosmos1m0czrla04f7rp3zg7dsgc4kla54q7pc4xt00l5", + "cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69", + time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC), + )) + suite.k.SetNextReasonID(ctx, 1, 1) + + suite.k.SaveReason(ctx, types.NewReason( + 1, + 1, + "Spam", + "This content is spam", + )) + }, + expBroken: true, + }, + { + name: "invalid reason breaks invariant", + store: func(ctx sdk.Context) { + suite.sk.SaveSubspace(ctx, subspacestypes.NewSubspace( + 1, + "Test subspace", + "This is a test subspace", + "cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69", + "cosmos1m0czrla04f7rp3zg7dsgc4kla54q7pc4xt00l5", + "cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69", + time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC), + )) + suite.k.SetNextReasonID(ctx, 1, 2) + + suite.k.SaveReason(ctx, types.NewReason( + 1, + 1, + "", + "This content is spam", + )) + }, + expBroken: true, + }, + { + name: "valid data does not break invariant", + store: func(ctx sdk.Context) { + suite.sk.SaveSubspace(ctx, subspacestypes.NewSubspace( + 1, + "Test subspace", + "This is a test subspace", + "cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69", + "cosmos1m0czrla04f7rp3zg7dsgc4kla54q7pc4xt00l5", + "cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69", + time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC), + )) + suite.k.SetNextReasonID(ctx, 1, 2) + + suite.k.SaveReason(ctx, types.NewReason( + 1, + 1, + "Spam", + "This content is spam", + )) + }, + expBroken: false, + }, + } + + for _, tc := range testCases { + tc := tc + suite.Run(tc.name, func() { + ctx, _ := suite.ctx.CacheContext() + if tc.store != nil { + tc.store(ctx) + } + + _, broken := keeper.ValidReasonsInvariant(suite.k)(ctx) + suite.Require().Equal(tc.expBroken, broken) + }) + } +} + +func (suite *KeeperTestsuite) TestValidReportsInvariant() { + testCases := []struct { + name string + store func(ctx sdk.Context) + expBroken bool + }{ + { + name: "missing subspace breaks invariant", + store: func(ctx sdk.Context) { + suite.k.SaveReport(ctx, types.NewReport( + 1, + 1, + []uint32{1}, + "This user is spamming", + types.NewUserTarget("cosmos1z0glns8fv5h0xgghg4nkq0jjy9gp0l682tcf79"), + "cosmos1ggzk8tnte9lmzgpvyzzdtmwmn6rjlct4spmjjd", + time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC), + )) + }, + expBroken: true, + }, + { + name: "missing reason breaks invariant", + store: func(ctx sdk.Context) { + suite.sk.SaveSubspace(ctx, subspacestypes.NewSubspace( + 1, + "Test subspace", + "This is a test subspace", + "cosmos1s0he0z3g92zwsxdj83h0ky9w463sx7gq9mqtgn", + "cosmos1s0he0z3g92zwsxdj83h0ky9w463sx7gq9mqtgn", + "cosmos1s0he0z3g92zwsxdj83h0ky9w463sx7gq9mqtgn", + time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC), + )) + + suite.k.SaveReport(ctx, types.NewReport( + 1, + 1, + []uint32{1}, + "This user is spamming", + types.NewUserTarget("cosmos1z0glns8fv5h0xgghg4nkq0jjy9gp0l682tcf79"), + "cosmos1ggzk8tnte9lmzgpvyzzdtmwmn6rjlct4spmjjd", + time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC), + )) + }, + expBroken: true, + }, + { + name: "missing next report id breaks invariant", + store: func(ctx sdk.Context) { + suite.sk.SaveSubspace(ctx, subspacestypes.NewSubspace( + 1, + "Test subspace", + "This is a test subspace", + "cosmos1s0he0z3g92zwsxdj83h0ky9w463sx7gq9mqtgn", + "cosmos1s0he0z3g92zwsxdj83h0ky9w463sx7gq9mqtgn", + "cosmos1s0he0z3g92zwsxdj83h0ky9w463sx7gq9mqtgn", + time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC), + )) + + suite.k.SaveReason(ctx, types.NewReason( + 1, + 1, + "Spam", + "This content is spam", + )) + + suite.k.SaveReport(ctx, types.NewReport( + 1, + 1, + []uint32{1}, + "This user is spamming", + types.NewUserTarget("cosmos1z0glns8fv5h0xgghg4nkq0jjy9gp0l682tcf79"), + "cosmos1ggzk8tnte9lmzgpvyzzdtmwmn6rjlct4spmjjd", + time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC), + )) + }, + expBroken: true, + }, + { + name: "invalid report id compared to next report id breaks invariant", + store: func(ctx sdk.Context) { + suite.sk.SaveSubspace(ctx, subspacestypes.NewSubspace( + 1, + "Test subspace", + "This is a test subspace", + "cosmos1s0he0z3g92zwsxdj83h0ky9w463sx7gq9mqtgn", + "cosmos1s0he0z3g92zwsxdj83h0ky9w463sx7gq9mqtgn", + "cosmos1s0he0z3g92zwsxdj83h0ky9w463sx7gq9mqtgn", + time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC), + )) + suite.k.SetNextReportID(ctx, 1, 1) + + suite.k.SaveReason(ctx, types.NewReason( + 1, + 1, + "Spam", + "This content is spam", + )) + + suite.k.SaveReport(ctx, types.NewReport( + 1, + 1, + []uint32{1}, + "This user is spamming", + types.NewUserTarget("cosmos1z0glns8fv5h0xgghg4nkq0jjy9gp0l682tcf79"), + "cosmos1ggzk8tnte9lmzgpvyzzdtmwmn6rjlct4spmjjd", + time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC), + )) + }, + expBroken: true, + }, + { + name: "missing post breaks invariant", + store: func(ctx sdk.Context) { + suite.sk.SaveSubspace(ctx, subspacestypes.NewSubspace( + 1, + "Test subspace", + "This is a test subspace", + "cosmos1s0he0z3g92zwsxdj83h0ky9w463sx7gq9mqtgn", + "cosmos1s0he0z3g92zwsxdj83h0ky9w463sx7gq9mqtgn", + "cosmos1s0he0z3g92zwsxdj83h0ky9w463sx7gq9mqtgn", + time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC), + )) + suite.k.SetNextReportID(ctx, 1, 2) + + suite.k.SaveReason(ctx, types.NewReason( + 1, + 1, + "Spam", + "This content is spam", + )) + + suite.k.SaveReport(ctx, types.NewReport( + 1, + 1, + []uint32{1}, + "This user is spamming", + types.NewPostTarget(1), + "cosmos1ggzk8tnte9lmzgpvyzzdtmwmn6rjlct4spmjjd", + time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC), + )) + }, + expBroken: true, + }, + { + name: "invalid report breaks invariant", + store: func(ctx sdk.Context) { + suite.sk.SaveSubspace(ctx, subspacestypes.NewSubspace( + 1, + "Test subspace", + "This is a test subspace", + "cosmos1s0he0z3g92zwsxdj83h0ky9w463sx7gq9mqtgn", + "cosmos1s0he0z3g92zwsxdj83h0ky9w463sx7gq9mqtgn", + "cosmos1s0he0z3g92zwsxdj83h0ky9w463sx7gq9mqtgn", + time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC), + )) + suite.k.SetNextReportID(ctx, 1, 2) + + suite.k.SaveReason(ctx, types.NewReason( + 1, + 1, + "Spam", + "This content is spam", + )) + + suite.k.SaveReport(ctx, types.NewReport( + 1, + 1, + []uint32{1}, + "This user is spamming", + types.NewUserTarget("cosmos1z0glns8fv5h0xgghg4nkq0jjy9gp0l682tcf79"), + "", + time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC), + )) + }, + expBroken: true, + }, + { + name: "valid data does not break invariant", + store: func(ctx sdk.Context) { + suite.sk.SaveSubspace(ctx, subspacestypes.NewSubspace( + 1, + "Test subspace", + "This is a test subspace", + "cosmos1s0he0z3g92zwsxdj83h0ky9w463sx7gq9mqtgn", + "cosmos1s0he0z3g92zwsxdj83h0ky9w463sx7gq9mqtgn", + "cosmos1s0he0z3g92zwsxdj83h0ky9w463sx7gq9mqtgn", + time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC), + )) + suite.k.SetNextReportID(ctx, 1, 2) + + suite.k.SaveReason(ctx, types.NewReason( + 1, + 1, + "Spam", + "This content is spam", + )) + + suite.pk.SavePost(ctx, poststypes.NewPost( + 1, + 0, + 1, + "External ID", + "This is a text", + "cosmos13t6y2nnugtshwuy0zkrq287a95lyy8vzleaxmd", + 1, + nil, + nil, + poststypes.REPLY_SETTING_EVERYONE, + time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC), + nil, + )) + + suite.k.SaveReport(ctx, types.NewReport( + 1, + 1, + []uint32{1}, + "This user is spamming", + types.NewPostTarget(1), + "cosmos1ggzk8tnte9lmzgpvyzzdtmwmn6rjlct4spmjjd", + time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC), + )) + }, + expBroken: false, + }, + } + + for _, tc := range testCases { + tc := tc + suite.Run(tc.name, func() { + ctx, _ := suite.ctx.CacheContext() + if tc.store != nil { + tc.store(ctx) + } + + _, broken := keeper.ValidReportsInvariant(suite.k)(ctx) + suite.Require().Equal(tc.expBroken, broken) + }) + } +} diff --git a/x/reports/keeper/keeper.go b/x/reports/keeper/keeper.go new file mode 100644 index 0000000000..782b3e7c27 --- /dev/null +++ b/x/reports/keeper/keeper.go @@ -0,0 +1,56 @@ +package keeper + +import ( + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + paramstypes "github.com/cosmos/cosmos-sdk/x/params/types" + "github.com/tendermint/tendermint/libs/log" + + "github.com/desmos-labs/desmos/v3/x/reports/types" +) + +type Keeper struct { + storeKey sdk.StoreKey + cdc codec.BinaryCodec + paramsSubspace paramstypes.Subspace + hooks types.ReportsHooks + + sk types.SubspacesKeeper + rk types.RelationshipsKeeper + pk types.PostsKeeper +} + +// NewKeeper creates a new instance of the Posts Keeper. +func NewKeeper( + cdc codec.BinaryCodec, storeKey sdk.StoreKey, paramsSubspace paramstypes.Subspace, + sk types.SubspacesKeeper, rk types.RelationshipsKeeper, pk types.PostsKeeper, +) Keeper { + if !paramsSubspace.HasKeyTable() { + paramsSubspace = paramsSubspace.WithKeyTable(types.ParamKeyTable()) + } + + return Keeper{ + storeKey: storeKey, + cdc: cdc, + paramsSubspace: paramsSubspace, + + sk: sk, + rk: rk, + pk: pk, + } +} + +// Logger returns a module-specific logger. +func (k Keeper) Logger(ctx sdk.Context) log.Logger { + return ctx.Logger().With("module", "x/"+types.ModuleName) +} + +// SetHooks allows to set the reports hooks +func (k *Keeper) SetHooks(rs types.ReportsHooks) *Keeper { + if k.hooks != nil { + panic("cannot set reports hooks twice") + } + + k.hooks = rs + return k +} diff --git a/x/reports/keeper/migrations.go b/x/reports/keeper/migrations.go new file mode 100644 index 0000000000..7cadaf3625 --- /dev/null +++ b/x/reports/keeper/migrations.go @@ -0,0 +1,29 @@ +package keeper + +// DONTCOVER + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + + v2 "github.com/desmos-labs/desmos/v3/x/reports/legacy/v2" + "github.com/desmos-labs/desmos/v3/x/reports/types" +) + +// Migrator is a struct for handling in-place store migrations. +type Migrator struct { + k Keeper + sk types.SubspacesKeeper +} + +// NewMigrator returns a new Migrator +func NewMigrator(keeper Keeper, sk types.SubspacesKeeper) Migrator { + return Migrator{ + k: keeper, + sk: sk, + } +} + +// Migrate1to2 migrates from version 1 to 2. +func (m Migrator) Migrate1to2(ctx sdk.Context) error { + return v2.MigrateStore(ctx, m.k.storeKey, m.sk) +} diff --git a/x/reports/keeper/msg_server.go b/x/reports/keeper/msg_server.go new file mode 100644 index 0000000000..cef96dde46 --- /dev/null +++ b/x/reports/keeper/msg_server.go @@ -0,0 +1,318 @@ +package keeper + +import ( + "context" + "fmt" + "time" + + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + + "github.com/desmos-labs/desmos/v3/x/reports/types" + subspacestypes "github.com/desmos-labs/desmos/v3/x/subspaces/types" +) + +type msgServer struct { + Keeper +} + +// NewMsgServerImpl returns an implementation of the stored MsgServer interface for the provided keeper +func NewMsgServerImpl(keeper Keeper) types.MsgServer { + return &msgServer{Keeper: keeper} +} + +var _ types.MsgServer = &msgServer{} + +// CreateReport defines the rpc method for Msg/CreateReport +func (k msgServer) CreateReport(goCtx context.Context, msg *types.MsgCreateReport) (*types.MsgCreateReportResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + + // Check if the subspace exists + if !k.HasSubspace(ctx, msg.SubspaceID) { + return nil, sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "subspace with id %d not found", msg.SubspaceID) + } + + // Check if the reasons exist + for _, reasonID := range msg.ReasonsIDs { + if !k.HasReason(ctx, msg.SubspaceID, reasonID) { + return nil, sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "reason with id %d not found inside subspace %d", reasonID, msg.SubspaceID) + } + } + + // Check the permission to report + if !k.HasPermission(ctx, msg.SubspaceID, msg.Reporter, subspacestypes.PermissionReportContent) { + return nil, sdkerrors.Wrap(subspacestypes.ErrPermissionDenied, "you cannot report content inside this subspace") + } + + target, ok := msg.Target.GetCachedValue().(types.ReportTarget) + if !ok { + return nil, sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "invalid target type: %s", msg.Target) + } + + // Make sure the report is not duplicated + if k.HasReported(ctx, msg.SubspaceID, msg.Reporter, target) { + return nil, sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "you have already reported this target") + } + + // Get the next report id + reportID, err := k.GetNextReportID(ctx, msg.SubspaceID) + if err != nil { + return nil, err + } + + // Create and validate the report + report := types.NewReport( + msg.SubspaceID, + reportID, + msg.ReasonsIDs, + msg.Message, + target, + msg.Reporter, + ctx.BlockTime(), + ) + err = k.ValidateReport(ctx, report) + if err != nil { + return nil, err + } + + // Store the report + k.SaveReport(ctx, report) + + // Update the id for the next report + k.SetNextReportID(ctx, msg.SubspaceID, report.ID+1) + + // Get the reporting event (different based on the target) + var reportEvent sdk.Event + switch target := target.(type) { + case *types.PostTarget: + reportEvent = sdk.NewEvent( + types.EventTypeReportPost, + sdk.NewAttribute(types.AttributeKeySubspaceID, fmt.Sprintf("%d", msg.SubspaceID)), + sdk.NewAttribute(types.AttributeKeyPostID, fmt.Sprintf("%d", target.PostID)), + sdk.NewAttribute(types.AttributeKeyReporter, msg.Reporter), + ) + case *types.UserTarget: + reportEvent = sdk.NewEvent( + types.EventTypeReportUser, + sdk.NewAttribute(types.AttributeKeySubspaceID, fmt.Sprintf("%d", msg.SubspaceID)), + sdk.NewAttribute(types.AttributeKeyUser, target.User), + sdk.NewAttribute(types.AttributeKeyReporter, msg.Reporter), + ) + default: + return nil, sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "invalid report target type: %T", msg.Target) + } + + ctx.EventManager().EmitEvents(sdk.Events{ + sdk.NewEvent( + sdk.EventTypeMessage, + sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory), + sdk.NewAttribute(sdk.AttributeKeyAction, sdk.MsgTypeURL(msg)), + sdk.NewAttribute(sdk.AttributeKeySender, msg.Reporter), + ), + sdk.NewEvent( + types.EventTypeCreateReport, + sdk.NewAttribute(types.AttributeKeySubspaceID, fmt.Sprintf("%d", msg.SubspaceID)), + sdk.NewAttribute(types.AttributeKeyReportID, fmt.Sprintf("%d", report.ID)), + sdk.NewAttribute(types.AttributeKeyReporter, msg.Reporter), + sdk.NewAttribute(types.AttributeKeyCreationTime, report.CreationDate.Format(time.RFC3339)), + ), + reportEvent, + }) + + return &types.MsgCreateReportResponse{ + ReportID: report.ID, + CreationDate: report.CreationDate, + }, nil +} + +// DeleteReport defines the rpc method for Msg/DeleteReport +func (k msgServer) DeleteReport(goCtx context.Context, msg *types.MsgDeleteReport) (*types.MsgDeleteReportResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + + // Check if the subspace exists + if !k.HasSubspace(ctx, msg.SubspaceID) { + return nil, sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "subspace with id %d not found", msg.SubspaceID) + } + + // Check if the report exists + report, found := k.GetReport(ctx, msg.SubspaceID, msg.ReportID) + if !found { + return nil, sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "report with id %d not found inside subspace %d", msg.ReportID, msg.SubspaceID) + } + + // Check the permission to delete reports + isModerator := k.HasPermission(ctx, msg.SubspaceID, msg.Signer, subspacestypes.PermissionManageReports) + canDelete := report.Reporter == msg.Signer && k.HasPermission(ctx, msg.SubspaceID, msg.Signer, subspacestypes.PermissionDeleteOwnReports) + if !isModerator && !canDelete { + return nil, sdkerrors.Wrap(subspacestypes.ErrPermissionDenied, "you cannot delete reports inside this subspace") + } + + // Delete the report + k.Keeper.DeleteReport(ctx, msg.SubspaceID, msg.ReportID) + + ctx.EventManager().EmitEvents(sdk.Events{ + sdk.NewEvent( + sdk.EventTypeMessage, + sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory), + sdk.NewAttribute(sdk.AttributeKeyAction, sdk.MsgTypeURL(msg)), + sdk.NewAttribute(sdk.AttributeKeySender, msg.Signer), + ), + sdk.NewEvent( + types.EventTypeDeleteReport, + sdk.NewAttribute(types.AttributeKeySubspaceID, fmt.Sprintf("%d", msg.SubspaceID)), + sdk.NewAttribute(types.AttributeKeyReportID, fmt.Sprintf("%d", msg.ReportID)), + ), + }) + + return &types.MsgDeleteReportResponse{}, nil +} + +// SupportStandardReason defines the rpc method for Msg/SupportStandardReason +func (k msgServer) SupportStandardReason(goCtx context.Context, msg *types.MsgSupportStandardReason) (*types.MsgSupportStandardReasonResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + + // Check if the subspace exists + if !k.HasSubspace(ctx, msg.SubspaceID) { + return nil, sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "subspace with id %d not found", msg.SubspaceID) + } + + // Check if the standard reason exists + standardReason, found := k.GetStandardReason(ctx, msg.StandardReasonID) + if !found { + return nil, sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "standard reason with id %d could not be found", msg.StandardReasonID) + } + + // Check the permission to manage reasons + if !k.HasPermission(ctx, msg.SubspaceID, msg.Signer, subspacestypes.PermissionManageReasons) { + return nil, sdkerrors.Wrap(subspacestypes.ErrPermissionDenied, "you cannot manage reasons inside this subspace") + } + + // Get the next reason id for the subspace + reasonID, err := k.GetNextReasonID(ctx, msg.SubspaceID) + if err != nil { + return nil, err + } + + // Create the reason and validate it + reason := types.NewReason(msg.SubspaceID, reasonID, standardReason.Title, standardReason.Description) + err = reason.Validate() + if err != nil { + return nil, sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, err.Error()) + } + + // Store the reason + k.SaveReason(ctx, reason) + + // Update the id for the next reason + k.SetNextReasonID(ctx, msg.SubspaceID, reason.ID+1) + + ctx.EventManager().EmitEvents(sdk.Events{ + sdk.NewEvent( + sdk.EventTypeMessage, + sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory), + sdk.NewAttribute(sdk.AttributeKeyAction, sdk.MsgTypeURL(msg)), + sdk.NewAttribute(sdk.AttributeKeySender, msg.Signer), + ), + sdk.NewEvent( + types.EventTypeSupportStandardReason, + sdk.NewAttribute(types.AttributeKeySubspaceID, fmt.Sprintf("%d", msg.SubspaceID)), + sdk.NewAttribute(types.AttributeKeyStandardReasonID, fmt.Sprintf("%d", msg.StandardReasonID)), + sdk.NewAttribute(types.AttributeKeyReasonID, fmt.Sprintf("%d", reason.ID)), + ), + }) + + return &types.MsgSupportStandardReasonResponse{ + ReasonsID: reason.ID, + }, nil +} + +// AddReason defines the rpc method for Msg/AddReason +func (k msgServer) AddReason(goCtx context.Context, msg *types.MsgAddReason) (*types.MsgAddReasonResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + + // Check if the subspace exists + if !k.HasSubspace(ctx, msg.SubspaceID) { + return nil, sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "subspace with id %d not found", msg.SubspaceID) + } + + // Check the permission to manage reasons + if !k.HasPermission(ctx, msg.SubspaceID, msg.Signer, subspacestypes.PermissionManageReasons) { + return nil, sdkerrors.Wrap(subspacestypes.ErrPermissionDenied, "you cannot manage reasons inside this subspace") + } + + // Get the next reason id for the subspace + reasonID, err := k.GetNextReasonID(ctx, msg.SubspaceID) + if err != nil { + return nil, err + } + + // Create the reason and validate it + reason := types.NewReason(msg.SubspaceID, reasonID, msg.Title, msg.Description) + err = reason.Validate() + if err != nil { + return nil, sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, err.Error()) + } + + // Store the reason + k.SaveReason(ctx, reason) + + // Update the id for the next reason + k.SetNextReasonID(ctx, msg.SubspaceID, reason.ID+1) + + ctx.EventManager().EmitEvents(sdk.Events{ + sdk.NewEvent( + sdk.EventTypeMessage, + sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory), + sdk.NewAttribute(sdk.AttributeKeyAction, sdk.MsgTypeURL(msg)), + sdk.NewAttribute(sdk.AttributeKeySender, msg.Signer), + ), + sdk.NewEvent( + types.EventTypeAddReason, + sdk.NewAttribute(types.AttributeKeySubspaceID, fmt.Sprintf("%d", msg.SubspaceID)), + sdk.NewAttribute(types.AttributeKeyReasonID, fmt.Sprintf("%d", reason.ID)), + ), + }) + + return &types.MsgAddReasonResponse{ + ReasonID: reason.ID, + }, nil +} + +// RemoveReason defines the rpc method for Msg/RemoveReason +func (k msgServer) RemoveReason(goCtx context.Context, msg *types.MsgRemoveReason) (*types.MsgRemoveReasonResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + + // Check if the subspace exists + if !k.HasSubspace(ctx, msg.SubspaceID) { + return nil, sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "subspace with id %d not found", msg.SubspaceID) + } + + // Check if the reason exists + if !k.HasReason(ctx, msg.SubspaceID, msg.ReasonID) { + return nil, sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "reason with id %d does not existing inside subspace %d", msg.ReasonID, msg.SubspaceID) + } + + // Check the permission to manage reasons + if !k.HasPermission(ctx, msg.SubspaceID, msg.Signer, subspacestypes.PermissionManageReasons) { + return nil, sdkerrors.Wrap(subspacestypes.ErrPermissionDenied, "you cannot manage reasons inside this subspace") + } + + // Delete the reason + k.DeleteReason(ctx, msg.SubspaceID, msg.ReasonID) + + ctx.EventManager().EmitEvents(sdk.Events{ + sdk.NewEvent( + sdk.EventTypeMessage, + sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory), + sdk.NewAttribute(sdk.AttributeKeyAction, sdk.MsgTypeURL(msg)), + sdk.NewAttribute(sdk.AttributeKeySender, msg.Signer), + ), + sdk.NewEvent( + types.EventTypeRemoveReason, + sdk.NewAttribute(types.AttributeKeySubspaceID, fmt.Sprintf("%d", msg.SubspaceID)), + sdk.NewAttribute(types.AttributeKeyReasonID, fmt.Sprintf("%d", msg.ReasonID)), + ), + }) + + return &types.MsgRemoveReasonResponse{}, nil +} diff --git a/x/reports/keeper/msg_server_test.go b/x/reports/keeper/msg_server_test.go new file mode 100644 index 0000000000..9592634627 --- /dev/null +++ b/x/reports/keeper/msg_server_test.go @@ -0,0 +1,1062 @@ +package keeper_test + +import ( + "time" + + poststypes "github.com/desmos-labs/desmos/v3/x/posts/types" + + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/desmos-labs/desmos/v3/x/reports/keeper" + "github.com/desmos-labs/desmos/v3/x/reports/types" + subspacestypes "github.com/desmos-labs/desmos/v3/x/subspaces/types" +) + +func (suite *KeeperTestsuite) TestMsgServer_CreateReport() { + testCases := []struct { + name string + setupCtx func(ctx sdk.Context) sdk.Context + store func(ctx sdk.Context) + msg *types.MsgCreateReport + shouldErr bool + expResponse *types.MsgCreateReportResponse + expEvents sdk.Events + check func(ctx sdk.Context) + }{ + { + name: "non existing subspace returns error", + msg: types.NewMsgCreateReport( + 1, + []uint32{1}, + "This content is spam!", + types.NewUserTarget("cosmos1ggzk8tnte9lmzgpvyzzdtmwmn6rjlct4spmjjd"), + "cosmos1qycmg40ju50fx2mcc82qtkzuswjs3mj3mqekeh", + ), + shouldErr: true, + }, + { + name: "non existing reason returns error", + store: func(ctx sdk.Context) { + suite.sk.SaveSubspace(ctx, subspacestypes.NewSubspace( + 1, + "Test subspace", + "This is a test subspace", + "cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69", + "cosmos1m0czrla04f7rp3zg7dsgc4kla54q7pc4xt00l5", + "cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69", + time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC), + )) + suite.k.SetNextReportID(ctx, 1, 1) + }, + msg: types.NewMsgCreateReport( + 1, + []uint32{1}, + "This content is spam!", + types.NewUserTarget("cosmos1ggzk8tnte9lmzgpvyzzdtmwmn6rjlct4spmjjd"), + "cosmos1qycmg40ju50fx2mcc82qtkzuswjs3mj3mqekeh", + ), + shouldErr: true, + }, + { + name: "no permission returns error", + store: func(ctx sdk.Context) { + suite.sk.SaveSubspace(ctx, subspacestypes.NewSubspace( + 1, + "Test subspace", + "This is a test subspace", + "cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69", + "cosmos1m0czrla04f7rp3zg7dsgc4kla54q7pc4xt00l5", + "cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69", + time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC), + )) + suite.k.SetNextReportID(ctx, 1, 1) + + suite.k.SaveReason(ctx, types.NewReason( + 1, + 1, + "Spam", + "This content is spam, or the user is spamming", + )) + }, + msg: types.NewMsgCreateReport( + 1, + []uint32{1}, + "This content is spam!", + types.NewUserTarget("cosmos1ggzk8tnte9lmzgpvyzzdtmwmn6rjlct4spmjjd"), + "cosmos1qycmg40ju50fx2mcc82qtkzuswjs3mj3mqekeh", + ), + shouldErr: true, + }, + { + name: "invalid report data returns error", + setupCtx: func(ctx sdk.Context) sdk.Context { + return ctx.WithBlockTime(time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC)) + }, + store: func(ctx sdk.Context) { + suite.sk.SaveSubspace(ctx, subspacestypes.NewSubspace( + 1, + "Test subspace", + "This is a test subspace", + "cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69", + "cosmos1m0czrla04f7rp3zg7dsgc4kla54q7pc4xt00l5", + "cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69", + time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC), + )) + suite.k.SetNextReportID(ctx, 1, 1) + + suite.sk.SetUserPermissions(ctx, + 1, + 0, + "cosmos1qycmg40ju50fx2mcc82qtkzuswjs3mj3mqekeh", + subspacestypes.PermissionReportContent, + ) + + suite.k.SaveReason(ctx, types.NewReason( + 1, + 1, + "Spam", + "This content is spam, or the user is spamming", + )) + }, + msg: types.NewMsgCreateReport( + 1, + []uint32{1}, + "This content is spam!", + types.NewUserTarget(""), + "cosmos1qycmg40ju50fx2mcc82qtkzuswjs3mj3mqekeh", + ), + shouldErr: true, + }, + { + name: "duplicated report returns error", + setupCtx: func(ctx sdk.Context) sdk.Context { + return ctx.WithBlockTime(time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC)) + }, + store: func(ctx sdk.Context) { + suite.sk.SaveSubspace(ctx, subspacestypes.NewSubspace( + 1, + "Test subspace", + "This is a test subspace", + "cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69", + "cosmos1m0czrla04f7rp3zg7dsgc4kla54q7pc4xt00l5", + "cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69", + time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC), + )) + suite.k.SetNextReportID(ctx, 1, 1) + + suite.sk.SetUserPermissions(ctx, + 1, + 0, + "cosmos1qycmg40ju50fx2mcc82qtkzuswjs3mj3mqekeh", + subspacestypes.PermissionReportContent, + ) + + suite.k.SaveReason(ctx, types.NewReason( + 1, + 1, + "Spam", + "This content is spam, or the user is spamming", + )) + + suite.k.SaveReport(ctx, types.NewReport( + 1, + 1, + []uint32{1}, + "This user is spamming", + types.NewUserTarget("cosmos1wprgptc8ktt0eemrn2znpxv8crdxm8tdpkdr7w"), + "cosmos1qycmg40ju50fx2mcc82qtkzuswjs3mj3mqekeh", + time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC), + )) + }, + msg: types.NewMsgCreateReport( + 1, + []uint32{1}, + "This user is spamming!", + types.NewUserTarget("cosmos1wprgptc8ktt0eemrn2znpxv8crdxm8tdpkdr7w"), + "cosmos1qycmg40ju50fx2mcc82qtkzuswjs3mj3mqekeh", + ), + shouldErr: true, + }, + { + name: "valid request works properly - user target", + setupCtx: func(ctx sdk.Context) sdk.Context { + return ctx.WithBlockTime(time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC)) + }, + store: func(ctx sdk.Context) { + suite.sk.SaveSubspace(ctx, subspacestypes.NewSubspace( + 1, + "Test subspace", + "This is a test subspace", + "cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69", + "cosmos1m0czrla04f7rp3zg7dsgc4kla54q7pc4xt00l5", + "cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69", + time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC), + )) + suite.k.SetNextReportID(ctx, 1, 1) + + suite.sk.SetUserPermissions(ctx, + 1, + 0, + "cosmos1qycmg40ju50fx2mcc82qtkzuswjs3mj3mqekeh", + subspacestypes.PermissionReportContent, + ) + + suite.k.SaveReason(ctx, types.NewReason( + 1, + 1, + "Spam", + "This content is spam, or the user is spamming", + )) + }, + msg: types.NewMsgCreateReport( + 1, + []uint32{1}, + "This content is spam!", + types.NewUserTarget("cosmos1ggzk8tnte9lmzgpvyzzdtmwmn6rjlct4spmjjd"), + "cosmos1qycmg40ju50fx2mcc82qtkzuswjs3mj3mqekeh", + ), + shouldErr: false, + expResponse: &types.MsgCreateReportResponse{ + ReportID: 1, + CreationDate: time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC), + }, + expEvents: sdk.Events{ + sdk.NewEvent( + sdk.EventTypeMessage, + sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory), + sdk.NewAttribute(sdk.AttributeKeyAction, sdk.MsgTypeURL(&types.MsgCreateReport{})), + sdk.NewAttribute(sdk.AttributeKeySender, "cosmos1qycmg40ju50fx2mcc82qtkzuswjs3mj3mqekeh"), + ), + sdk.NewEvent( + types.EventTypeCreateReport, + sdk.NewAttribute(types.AttributeKeySubspaceID, "1"), + sdk.NewAttribute(types.AttributeKeyReportID, "1"), + sdk.NewAttribute(types.AttributeKeyReporter, "cosmos1qycmg40ju50fx2mcc82qtkzuswjs3mj3mqekeh"), + sdk.NewAttribute(types.AttributeKeyCreationTime, time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC).Format(time.RFC3339)), + ), + sdk.NewEvent( + types.EventTypeReportUser, + sdk.NewAttribute(types.AttributeKeySubspaceID, "1"), + sdk.NewAttribute(types.AttributeKeyUser, "cosmos1ggzk8tnte9lmzgpvyzzdtmwmn6rjlct4spmjjd"), + sdk.NewAttribute(types.AttributeKeyReporter, "cosmos1qycmg40ju50fx2mcc82qtkzuswjs3mj3mqekeh"), + ), + }, + check: func(ctx sdk.Context) { + store := ctx.KVStore(suite.storeKey) + nextReportID := types.GetReportIDFromBytes(store.Get(types.NextReportIDStoreKey(1))) + suite.Require().Equal(uint64(2), nextReportID) + }, + }, + { + name: "valid request works properly - post target", + setupCtx: func(ctx sdk.Context) sdk.Context { + return ctx.WithBlockTime(time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC)) + }, + store: func(ctx sdk.Context) { + suite.sk.SaveSubspace(ctx, subspacestypes.NewSubspace( + 1, + "Test subspace", + "This is a test subspace", + "cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69", + "cosmos1m0czrla04f7rp3zg7dsgc4kla54q7pc4xt00l5", + "cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69", + time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC), + )) + suite.k.SetNextReportID(ctx, 1, 1) + + suite.sk.SetUserPermissions(ctx, + 1, + 0, + "cosmos1qycmg40ju50fx2mcc82qtkzuswjs3mj3mqekeh", + subspacestypes.PermissionReportContent, + ) + + suite.k.SaveReason(ctx, types.NewReason( + 1, + 1, + "Spam", + "This content is spam, or the user is spamming", + )) + + suite.pk.SavePost(ctx, poststypes.NewPost( + 1, + 0, + 1, + "External ID", + "This is a text", + "cosmos13t6y2nnugtshwuy0zkrq287a95lyy8vzleaxmd", + 1, + nil, + nil, + poststypes.REPLY_SETTING_EVERYONE, + time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC), + nil, + )) + }, + msg: types.NewMsgCreateReport( + 1, + []uint32{1}, + "This content is spam!", + types.NewPostTarget(1), + "cosmos1qycmg40ju50fx2mcc82qtkzuswjs3mj3mqekeh", + ), + shouldErr: false, + expResponse: &types.MsgCreateReportResponse{ + ReportID: 1, + CreationDate: time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC), + }, + expEvents: sdk.Events{ + sdk.NewEvent( + sdk.EventTypeMessage, + sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory), + sdk.NewAttribute(sdk.AttributeKeyAction, sdk.MsgTypeURL(&types.MsgCreateReport{})), + sdk.NewAttribute(sdk.AttributeKeySender, "cosmos1qycmg40ju50fx2mcc82qtkzuswjs3mj3mqekeh"), + ), + sdk.NewEvent( + types.EventTypeCreateReport, + sdk.NewAttribute(types.AttributeKeySubspaceID, "1"), + sdk.NewAttribute(types.AttributeKeyReportID, "1"), + sdk.NewAttribute(types.AttributeKeyReporter, "cosmos1qycmg40ju50fx2mcc82qtkzuswjs3mj3mqekeh"), + sdk.NewAttribute(types.AttributeKeyCreationTime, time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC).Format(time.RFC3339)), + ), + sdk.NewEvent( + types.EventTypeReportPost, + sdk.NewAttribute(types.AttributeKeySubspaceID, "1"), + sdk.NewAttribute(types.AttributeKeyPostID, "1"), + sdk.NewAttribute(types.AttributeKeyReporter, "cosmos1qycmg40ju50fx2mcc82qtkzuswjs3mj3mqekeh"), + ), + }, + check: func(ctx sdk.Context) { + store := ctx.KVStore(suite.storeKey) + nextReportID := types.GetReportIDFromBytes(store.Get(types.NextReportIDStoreKey(1))) + suite.Require().Equal(uint64(2), nextReportID) + }, + }, + } + + for _, tc := range testCases { + tc := tc + suite.Run(tc.name, func() { + ctx, _ := suite.ctx.CacheContext() + if tc.setupCtx != nil { + ctx = tc.setupCtx(ctx) + } + if tc.store != nil { + tc.store(ctx) + } + + msgServer := keeper.NewMsgServerImpl(suite.k) + res, err := msgServer.CreateReport(sdk.WrapSDKContext(ctx), tc.msg) + if tc.shouldErr { + suite.Require().Error(err) + } else { + suite.Require().NoError(err) + suite.Require().Equal(tc.expResponse, res) + suite.Require().Equal(tc.expEvents, ctx.EventManager().Events()) + + if tc.check != nil { + tc.check(ctx) + } + } + }) + } +} + +func (suite *KeeperTestsuite) TestMsgServer_DeleteReport() { + testCases := []struct { + name string + store func(ctx sdk.Context) + msg *types.MsgDeleteReport + shouldErr bool + expEvents sdk.Events + }{ + { + name: "non existing subspace returns error", + msg: types.NewMsgDeleteReport( + 1, + 1, + "cosmos1qycmg40ju50fx2mcc82qtkzuswjs3mj3mqekeh", + ), + shouldErr: true, + }, + { + name: "non existing report returns error", + store: func(ctx sdk.Context) { + suite.sk.SaveSubspace(ctx, subspacestypes.NewSubspace( + 1, + "Test subspace", + "This is a test subspace", + "cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69", + "cosmos1m0czrla04f7rp3zg7dsgc4kla54q7pc4xt00l5", + "cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69", + time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC), + )) + }, + msg: types.NewMsgDeleteReport( + 1, + 1, + "cosmos1qycmg40ju50fx2mcc82qtkzuswjs3mj3mqekeh", + ), + shouldErr: true, + }, + { + name: "no permission returns error", + store: func(ctx sdk.Context) { + suite.sk.SaveSubspace(ctx, subspacestypes.NewSubspace( + 1, + "Test subspace", + "This is a test subspace", + "cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69", + "cosmos1m0czrla04f7rp3zg7dsgc4kla54q7pc4xt00l5", + "cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69", + time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC), + )) + + suite.k.SaveReport(ctx, types.NewReport( + 1, + 1, + []uint32{1}, + "This content is spam", + types.NewUserTarget("cosmos1pjffdtweghpyxru9alssyqtdkq8mn6sepgstgm"), + "cosmos1qycmg40ju50fx2mcc82qtkzuswjs3mj3mqekeh", + time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC), + )) + }, + msg: types.NewMsgDeleteReport( + 1, + 1, + "cosmos1qycmg40ju50fx2mcc82qtkzuswjs3mj3mqekeh", + ), + shouldErr: true, + }, + { + name: "invalid signer returns error", + store: func(ctx sdk.Context) { + suite.sk.SaveSubspace(ctx, subspacestypes.NewSubspace( + 1, + "Test subspace", + "This is a test subspace", + "cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69", + "cosmos1m0czrla04f7rp3zg7dsgc4kla54q7pc4xt00l5", + "cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69", + time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC), + )) + + suite.k.SaveReport(ctx, types.NewReport( + 1, + 1, + []uint32{1}, + "This content is spam", + types.NewUserTarget("cosmos1pjffdtweghpyxru9alssyqtdkq8mn6sepgstgm"), + "cosmos1zkmf50jq4lzvhvp5ekl0sdf2p4g3v9v8edt24z", + time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC), + )) + }, + msg: types.NewMsgDeleteReport( + 1, + 1, + "cosmos1qycmg40ju50fx2mcc82qtkzuswjs3mj3mqekeh", + ), + shouldErr: true, + }, + { + name: "report creator can delete the report properly", + store: func(ctx sdk.Context) { + suite.sk.SaveSubspace(ctx, subspacestypes.NewSubspace( + 1, + "Test subspace", + "This is a test subspace", + "cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69", + "cosmos1m0czrla04f7rp3zg7dsgc4kla54q7pc4xt00l5", + "cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69", + time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC), + )) + + suite.sk.SetUserPermissions(ctx, + 1, + 0, + "cosmos1qycmg40ju50fx2mcc82qtkzuswjs3mj3mqekeh", + subspacestypes.PermissionDeleteOwnReports, + ) + + suite.k.SaveReport(ctx, types.NewReport( + 1, + 1, + []uint32{1}, + "This content is spam", + types.NewUserTarget("cosmos1pjffdtweghpyxru9alssyqtdkq8mn6sepgstgm"), + "cosmos1qycmg40ju50fx2mcc82qtkzuswjs3mj3mqekeh", + time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC), + )) + }, + msg: types.NewMsgDeleteReport( + 1, + 1, + "cosmos1qycmg40ju50fx2mcc82qtkzuswjs3mj3mqekeh", + ), + shouldErr: false, + expEvents: sdk.Events{ + sdk.NewEvent( + sdk.EventTypeMessage, + sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory), + sdk.NewAttribute(sdk.AttributeKeyAction, sdk.MsgTypeURL(&types.MsgDeleteReport{})), + sdk.NewAttribute(sdk.AttributeKeySender, "cosmos1qycmg40ju50fx2mcc82qtkzuswjs3mj3mqekeh"), + ), + sdk.NewEvent( + types.EventTypeDeleteReport, + sdk.NewAttribute(types.AttributeKeySubspaceID, "1"), + sdk.NewAttribute(types.AttributeKeyReportID, "1"), + ), + }, + }, + { + name: "moderator can delete the report properly", + store: func(ctx sdk.Context) { + suite.sk.SaveSubspace(ctx, subspacestypes.NewSubspace( + 1, + "Test subspace", + "This is a test subspace", + "cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69", + "cosmos1m0czrla04f7rp3zg7dsgc4kla54q7pc4xt00l5", + "cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69", + time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC), + )) + + suite.sk.SetUserPermissions(ctx, + 1, + 0, + "cosmos1qycmg40ju50fx2mcc82qtkzuswjs3mj3mqekeh", + subspacestypes.PermissionManageReports, + ) + + suite.k.SaveReport(ctx, types.NewReport( + 1, + 1, + []uint32{1}, + "This content is spam", + types.NewUserTarget("cosmos1pjffdtweghpyxru9alssyqtdkq8mn6sepgstgm"), + "cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69", + time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC), + )) + }, + msg: types.NewMsgDeleteReport( + 1, + 1, + "cosmos1qycmg40ju50fx2mcc82qtkzuswjs3mj3mqekeh", + ), + shouldErr: false, + expEvents: sdk.Events{ + sdk.NewEvent( + sdk.EventTypeMessage, + sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory), + sdk.NewAttribute(sdk.AttributeKeyAction, sdk.MsgTypeURL(&types.MsgDeleteReport{})), + sdk.NewAttribute(sdk.AttributeKeySender, "cosmos1qycmg40ju50fx2mcc82qtkzuswjs3mj3mqekeh"), + ), + sdk.NewEvent( + types.EventTypeDeleteReport, + sdk.NewAttribute(types.AttributeKeySubspaceID, "1"), + sdk.NewAttribute(types.AttributeKeyReportID, "1"), + ), + }, + }, + } + + for _, tc := range testCases { + tc := tc + suite.Run(tc.name, func() { + ctx, _ := suite.ctx.CacheContext() + if tc.store != nil { + tc.store(ctx) + } + + msgServer := keeper.NewMsgServerImpl(suite.k) + _, err := msgServer.DeleteReport(sdk.WrapSDKContext(ctx), tc.msg) + if tc.shouldErr { + suite.Require().Error(err) + } else { + suite.Require().NoError(err) + } + }) + } +} + +func (suite *KeeperTestsuite) TestMsgServer_SupportStandardReason() { + testCases := []struct { + name string + store func(ctx sdk.Context) + msg *types.MsgSupportStandardReason + shouldErr bool + expResponse *types.MsgSupportStandardReasonResponse + expEvents sdk.Events + check func(ctx sdk.Context) + }{ + { + name: "non existing subspace returns error", + msg: types.NewMsgSupportStandardReason( + 1, + 1, + "cosmos1qycmg40ju50fx2mcc82qtkzuswjs3mj3mqekeh", + ), + shouldErr: true, + }, + { + name: "non existing standard reason returns error", + store: func(ctx sdk.Context) { + suite.sk.SaveSubspace(ctx, subspacestypes.NewSubspace( + 1, + "Test subspace", + "This is a test subspace", + "cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69", + "cosmos1m0czrla04f7rp3zg7dsgc4kla54q7pc4xt00l5", + "cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69", + time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC), + )) + + suite.k.SetParams(ctx, types.NewParams(nil)) + }, + msg: types.NewMsgSupportStandardReason( + 1, + 1, + "cosmos1qycmg40ju50fx2mcc82qtkzuswjs3mj3mqekeh", + ), + shouldErr: true, + }, + { + name: "no permission returns error", + store: func(ctx sdk.Context) { + suite.sk.SaveSubspace(ctx, subspacestypes.NewSubspace( + 1, + "Test subspace", + "This is a test subspace", + "cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69", + "cosmos1m0czrla04f7rp3zg7dsgc4kla54q7pc4xt00l5", + "cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69", + time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC), + )) + + suite.k.SetParams(ctx, types.NewParams([]types.StandardReason{ + types.NewStandardReason(1, "Spam", "This content is spam"), + })) + }, + msg: types.NewMsgSupportStandardReason( + 1, + 1, + "cosmos1qycmg40ju50fx2mcc82qtkzuswjs3mj3mqekeh", + ), + shouldErr: true, + }, + { + name: "not found next reason id returns error", + store: func(ctx sdk.Context) { + suite.sk.SaveSubspace(ctx, subspacestypes.NewSubspace( + 1, + "Test subspace", + "This is a test subspace", + "cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69", + "cosmos1m0czrla04f7rp3zg7dsgc4kla54q7pc4xt00l5", + "cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69", + time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC), + )) + + suite.sk.SetUserPermissions(ctx, + 1, + 0, + "cosmos1qycmg40ju50fx2mcc82qtkzuswjs3mj3mqekeh", + subspacestypes.PermissionManageReasons, + ) + + suite.k.SetParams(ctx, types.NewParams([]types.StandardReason{ + types.NewStandardReason(1, "Spam", "This content is spam"), + })) + }, + msg: types.NewMsgSupportStandardReason( + 1, + 1, + "cosmos1qycmg40ju50fx2mcc82qtkzuswjs3mj3mqekeh", + ), + shouldErr: true, + }, + { + name: "valid request returns no error", + store: func(ctx sdk.Context) { + suite.sk.SaveSubspace(ctx, subspacestypes.NewSubspace( + 1, + "Test subspace", + "This is a test subspace", + "cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69", + "cosmos1m0czrla04f7rp3zg7dsgc4kla54q7pc4xt00l5", + "cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69", + time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC), + )) + suite.k.SetNextReasonID(ctx, 1, 1) + + suite.sk.SetUserPermissions(ctx, + 1, + 0, + "cosmos1qycmg40ju50fx2mcc82qtkzuswjs3mj3mqekeh", + subspacestypes.PermissionManageReasons, + ) + + suite.k.SetParams(ctx, types.NewParams([]types.StandardReason{ + types.NewStandardReason(1, "Spam", "This content is spam"), + })) + }, + msg: types.NewMsgSupportStandardReason( + 1, + 1, + "cosmos1qycmg40ju50fx2mcc82qtkzuswjs3mj3mqekeh", + ), + shouldErr: false, + expResponse: &types.MsgSupportStandardReasonResponse{ + ReasonsID: 1, + }, + expEvents: sdk.Events{ + sdk.NewEvent( + sdk.EventTypeMessage, + sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory), + sdk.NewAttribute(sdk.AttributeKeyAction, sdk.MsgTypeURL(&types.MsgSupportStandardReason{})), + sdk.NewAttribute(sdk.AttributeKeySender, "cosmos1qycmg40ju50fx2mcc82qtkzuswjs3mj3mqekeh"), + ), + sdk.NewEvent( + types.EventTypeSupportStandardReason, + sdk.NewAttribute(types.AttributeKeySubspaceID, "1"), + sdk.NewAttribute(types.AttributeKeyStandardReasonID, "1"), + sdk.NewAttribute(types.AttributeKeyReasonID, "1"), + ), + }, + check: func(ctx sdk.Context) { + store := ctx.KVStore(suite.storeKey) + nextReasonID := types.GetReasonIDFromBytes(store.Get(types.NextReasonIDStoreKey(1))) + suite.Require().Equal(uint32(2), nextReasonID) + }, + }, + } + + for _, tc := range testCases { + tc := tc + suite.Run(tc.name, func() { + ctx, _ := suite.ctx.CacheContext() + if tc.store != nil { + tc.store(ctx) + } + + msgServer := keeper.NewMsgServerImpl(suite.k) + res, err := msgServer.SupportStandardReason(sdk.WrapSDKContext(ctx), tc.msg) + if tc.shouldErr { + suite.Require().Error(err) + } else { + suite.Require().NoError(err) + suite.Require().Equal(tc.expResponse, res) + suite.Require().Equal(tc.expEvents, ctx.EventManager().Events()) + + if tc.check != nil { + tc.check(ctx) + } + } + }) + } +} + +func (suite *KeeperTestsuite) TestMsgServer_AddReason() { + testCases := []struct { + name string + store func(ctx sdk.Context) + msg *types.MsgAddReason + shouldErr bool + expResponse *types.MsgAddReasonResponse + expEvents sdk.Events + check func(ctx sdk.Context) + }{ + { + name: "non existing subspace returns error", + msg: types.NewMsgAddReason( + 1, + "Spam", + "This content is spam", + "cosmos1qycmg40ju50fx2mcc82qtkzuswjs3mj3mqekeh", + ), + shouldErr: true, + }, + { + name: "no permission returns error", + store: func(ctx sdk.Context) { + suite.sk.SaveSubspace(ctx, subspacestypes.NewSubspace( + 1, + "Test subspace", + "This is a test subspace", + "cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69", + "cosmos1m0czrla04f7rp3zg7dsgc4kla54q7pc4xt00l5", + "cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69", + time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC), + )) + }, + msg: types.NewMsgAddReason( + 1, + "Spam", + "This content is spam", + "cosmos1qycmg40ju50fx2mcc82qtkzuswjs3mj3mqekeh", + ), + shouldErr: true, + }, + { + name: "no next reason id returns error", + store: func(ctx sdk.Context) { + suite.sk.SaveSubspace(ctx, subspacestypes.NewSubspace( + 1, + "Test subspace", + "This is a test subspace", + "cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69", + "cosmos1m0czrla04f7rp3zg7dsgc4kla54q7pc4xt00l5", + "cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69", + time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC), + )) + + suite.sk.SetUserPermissions(ctx, + 1, + 0, + "cosmos1qycmg40ju50fx2mcc82qtkzuswjs3mj3mqekeh", + subspacestypes.PermissionManageReasons, + ) + }, + msg: types.NewMsgAddReason( + 1, + "Spam", + "This content is spam", + "cosmos1qycmg40ju50fx2mcc82qtkzuswjs3mj3mqekeh", + ), + shouldErr: true, + }, + { + name: "invalid reason returns error", + store: func(ctx sdk.Context) { + suite.sk.SaveSubspace(ctx, subspacestypes.NewSubspace( + 1, + "Test subspace", + "This is a test subspace", + "cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69", + "cosmos1m0czrla04f7rp3zg7dsgc4kla54q7pc4xt00l5", + "cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69", + time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC), + )) + suite.k.SetNextReasonID(ctx, 1, 1) + + suite.sk.SetUserPermissions(ctx, + 1, + 0, + "cosmos1qycmg40ju50fx2mcc82qtkzuswjs3mj3mqekeh", + subspacestypes.PermissionManageReasons, + ) + }, + msg: types.NewMsgAddReason( + 1, + "", + "This content is spam", + "cosmos1qycmg40ju50fx2mcc82qtkzuswjs3mj3mqekeh", + ), + shouldErr: true, + }, + { + name: "correct request returns error", + store: func(ctx sdk.Context) { + suite.sk.SaveSubspace(ctx, subspacestypes.NewSubspace( + 1, + "Test subspace", + "This is a test subspace", + "cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69", + "cosmos1m0czrla04f7rp3zg7dsgc4kla54q7pc4xt00l5", + "cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69", + time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC), + )) + suite.k.SetNextReasonID(ctx, 1, 1) + + suite.sk.SetUserPermissions(ctx, + 1, + 0, + "cosmos1qycmg40ju50fx2mcc82qtkzuswjs3mj3mqekeh", + subspacestypes.PermissionManageReasons, + ) + }, + msg: types.NewMsgAddReason( + 1, + "Spam", + "This content is spam", + "cosmos1qycmg40ju50fx2mcc82qtkzuswjs3mj3mqekeh", + ), + shouldErr: false, + expResponse: &types.MsgAddReasonResponse{ + ReasonID: 1, + }, + expEvents: sdk.Events{ + sdk.NewEvent( + sdk.EventTypeMessage, + sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory), + sdk.NewAttribute(sdk.AttributeKeyAction, sdk.MsgTypeURL(&types.MsgAddReason{})), + sdk.NewAttribute(sdk.AttributeKeySender, "cosmos1qycmg40ju50fx2mcc82qtkzuswjs3mj3mqekeh"), + ), + sdk.NewEvent( + types.EventTypeAddReason, + sdk.NewAttribute(types.AttributeKeySubspaceID, "1"), + sdk.NewAttribute(types.AttributeKeyReasonID, "1"), + ), + }, + check: func(ctx sdk.Context) { + store := ctx.KVStore(suite.storeKey) + nextReasonID := types.GetReasonIDFromBytes(store.Get(types.NextReasonIDStoreKey(1))) + suite.Require().Equal(uint64(2), nextReasonID) + }, + }, + } + + for _, tc := range testCases { + tc := tc + suite.Run(tc.name, func() { + ctx, _ := suite.ctx.CacheContext() + if tc.store != nil { + tc.store(ctx) + } + + msgServer := keeper.NewMsgServerImpl(suite.k) + res, err := msgServer.AddReason(sdk.WrapSDKContext(ctx), tc.msg) + if tc.shouldErr { + suite.Require().Error(err) + } else { + suite.Require().NoError(err) + suite.Require().Equal(tc.expResponse, res) + suite.Require().Equal(tc.expEvents, ctx.EventManager().Events()) + } + }) + } +} + +func (suite *KeeperTestsuite) TestMsgServer_RemoveReason() { + testCases := []struct { + name string + store func(ctx sdk.Context) + msg *types.MsgRemoveReason + shouldErr bool + expEvents sdk.Events + }{ + { + name: "non existing subspace returns error", + msg: types.NewMsgRemoveReason( + 1, + 1, + "cosmos1qycmg40ju50fx2mcc82qtkzuswjs3mj3mqekeh", + ), + shouldErr: true, + }, + { + name: "non existing reason returns error", + store: func(ctx sdk.Context) { + suite.sk.SaveSubspace(ctx, subspacestypes.NewSubspace( + 1, + "Test subspace", + "This is a test subspace", + "cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69", + "cosmos1m0czrla04f7rp3zg7dsgc4kla54q7pc4xt00l5", + "cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69", + time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC), + )) + }, + msg: types.NewMsgRemoveReason( + 1, + 1, + "cosmos1qycmg40ju50fx2mcc82qtkzuswjs3mj3mqekeh", + ), + shouldErr: true, + }, + { + name: "no permission returns error", + store: func(ctx sdk.Context) { + suite.sk.SaveSubspace(ctx, subspacestypes.NewSubspace( + 1, + "Test subspace", + "This is a test subspace", + "cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69", + "cosmos1m0czrla04f7rp3zg7dsgc4kla54q7pc4xt00l5", + "cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69", + time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC), + )) + + suite.k.SaveReason(ctx, types.NewReason( + 1, + 1, + "Spam", + "This content is spam", + )) + }, + msg: types.NewMsgRemoveReason( + 1, + 1, + "cosmos1qycmg40ju50fx2mcc82qtkzuswjs3mj3mqekeh", + ), + shouldErr: true, + }, + { + name: "valid request works properly", + store: func(ctx sdk.Context) { + suite.sk.SaveSubspace(ctx, subspacestypes.NewSubspace( + 1, + "Test subspace", + "This is a test subspace", + "cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69", + "cosmos1m0czrla04f7rp3zg7dsgc4kla54q7pc4xt00l5", + "cosmos1qzskhrcjnkdz2ln4yeafzsdwht8ch08j4wed69", + time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC), + )) + + suite.k.SaveReason(ctx, types.NewReason( + 1, + 1, + "Spam", + "This content is spam", + )) + + suite.sk.SetUserPermissions(ctx, + 1, + 0, + "cosmos1qycmg40ju50fx2mcc82qtkzuswjs3mj3mqekeh", + subspacestypes.PermissionManageReasons, + ) + }, + msg: types.NewMsgRemoveReason( + 1, + 1, + "cosmos1qycmg40ju50fx2mcc82qtkzuswjs3mj3mqekeh", + ), + shouldErr: false, + expEvents: sdk.Events{ + sdk.NewEvent( + sdk.EventTypeMessage, + sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory), + sdk.NewAttribute(sdk.AttributeKeyAction, sdk.MsgTypeURL(&types.MsgRemoveReason{})), + sdk.NewAttribute(sdk.AttributeKeySender, "cosmos1qycmg40ju50fx2mcc82qtkzuswjs3mj3mqekeh"), + ), + sdk.NewEvent( + types.EventTypeRemoveReason, + sdk.NewAttribute(types.AttributeKeySubspaceID, "1"), + sdk.NewAttribute(types.AttributeKeyReasonID, "1"), + ), + }, + }, + } + + for _, tc := range testCases { + tc := tc + suite.Run(tc.name, func() { + ctx, _ := suite.ctx.CacheContext() + if tc.store != nil { + tc.store(ctx) + } + + msgServer := keeper.NewMsgServerImpl(suite.k) + _, err := msgServer.RemoveReason(sdk.WrapSDKContext(ctx), tc.msg) + if tc.shouldErr { + suite.Require().Error(err) + } else { + suite.Require().NoError(err) + suite.Require().Equal(tc.expEvents, ctx.EventManager().Events()) + } + }) + } +} diff --git a/x/reports/keeper/params.go b/x/reports/keeper/params.go new file mode 100644 index 0000000000..ee7e9841e8 --- /dev/null +++ b/x/reports/keeper/params.go @@ -0,0 +1,18 @@ +package keeper + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/desmos-labs/desmos/v3/x/reports/types" +) + +// SetParams sets the params on the store +func (k Keeper) SetParams(ctx sdk.Context, params types.Params) { + k.paramsSubspace.SetParamSet(ctx, ¶ms) +} + +// GetParams returns the params from the store +func (k Keeper) GetParams(ctx sdk.Context) (p types.Params) { + k.paramsSubspace.GetParamSet(ctx, &p) + return p +} diff --git a/x/reports/keeper/params_test.go b/x/reports/keeper/params_test.go new file mode 100644 index 0000000000..900c65117d --- /dev/null +++ b/x/reports/keeper/params_test.go @@ -0,0 +1,95 @@ +package keeper_test + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/desmos-labs/desmos/v3/x/reports/types" +) + +func (suite *KeeperTestsuite) TestKeeper_SetParams() { + testCases := []struct { + name string + store func(ctx sdk.Context) + params types.Params + check func(ctx sdk.Context) + }{ + { + name: "default params are saved correctly", + params: types.DefaultParams(), + check: func(ctx sdk.Context) { + stored := suite.k.GetParams(ctx) + suite.Require().Equal(types.DefaultParams(), stored) + }, + }, + { + name: "params are overridden properly", + store: func(ctx sdk.Context) { + suite.k.SetParams(ctx, types.DefaultParams()) + }, + params: types.NewParams(types.NewStandardReasons( + types.NewStandardReason(1, "Pornography", "This content contains pornography"), + )), + check: func(ctx sdk.Context) { + store := suite.k.GetParams(ctx) + suite.Require().Equal(types.NewParams(types.NewStandardReasons( + types.NewStandardReason(1, "Pornography", "This content contains pornography"), + )), store) + }, + }, + } + + for _, tc := range testCases { + tc := tc + suite.Run(tc.name, func() { + ctx, _ := suite.ctx.CacheContext() + if tc.store != nil { + tc.store(ctx) + } + + suite.k.SetParams(ctx, tc.params) + if tc.check != nil { + tc.check(ctx) + } + }) + } +} + +func (suite *KeeperTestsuite) TestKeeper_GetParams() { + testCases := []struct { + name string + store func(ctx sdk.Context) + expParams types.Params + }{ + { + name: "default params are returned properly", + store: func(ctx sdk.Context) { + suite.k.SetParams(ctx, types.DefaultParams()) + }, + expParams: types.DefaultParams(), + }, + { + name: "custom params are returned properly", + store: func(ctx sdk.Context) { + suite.k.SetParams(ctx, types.NewParams(types.NewStandardReasons( + types.NewStandardReason(1, "Pornography", "This content contains pornography"), + ))) + }, + expParams: types.NewParams(types.NewStandardReasons( + types.NewStandardReason(1, "Pornography", "This content contains pornography"), + )), + }, + } + + for _, tc := range testCases { + tc := tc + suite.Run(tc.name, func() { + ctx, _ := suite.ctx.CacheContext() + if tc.store != nil { + tc.store(ctx) + } + + params := suite.k.GetParams(ctx) + suite.Require().True(tc.expParams.Equal(params)) + }) + } +} diff --git a/x/reports/keeper/reasons.go b/x/reports/keeper/reasons.go new file mode 100644 index 0000000000..a12ae72fc5 --- /dev/null +++ b/x/reports/keeper/reasons.go @@ -0,0 +1,92 @@ +package keeper + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + + "github.com/desmos-labs/desmos/v3/x/reports/types" +) + +// SetNextReasonID sets the new reason id for the given subspace to the store +func (k Keeper) SetNextReasonID(ctx sdk.Context, subspaceID uint64, reasonID uint32) { + store := ctx.KVStore(k.storeKey) + store.Set(types.NextReasonIDStoreKey(subspaceID), types.GetReasonIDBytes(reasonID)) +} + +// HasNextReasonID tells whether the next reason id exists for the given subspace +func (k Keeper) HasNextReasonID(ctx sdk.Context, subspaceID uint64) bool { + store := ctx.KVStore(k.storeKey) + return store.Has(types.NextReasonIDStoreKey(subspaceID)) +} + +// GetNextReasonID gets the highest reason id for the given subspace +func (k Keeper) GetNextReasonID(ctx sdk.Context, subspaceID uint64) (reasonID uint32, err error) { + store := ctx.KVStore(k.storeKey) + bz := store.Get(types.NextReasonIDStoreKey(subspaceID)) + if bz == nil { + return 0, sdkerrors.Wrapf(types.ErrInvalidGenesis, "initial reason id not set for subspace %d", subspaceID) + } + + reasonID = types.GetReasonIDFromBytes(bz) + return reasonID, nil +} + +// DeleteNextReasonID removes the reason id key for the given subspace +func (k Keeper) DeleteNextReasonID(ctx sdk.Context, subspaceID uint64) { + store := ctx.KVStore(k.storeKey) + store.Delete(types.NextReasonIDStoreKey(subspaceID)) +} + +// -------------------------------------------------------------------------------------------------------------------- + +// SaveReason saves the given reason inside the current context +func (k Keeper) SaveReason(ctx sdk.Context, reason types.Reason) { + store := ctx.KVStore(k.storeKey) + + // Store the reason + store.Set(types.ReasonStoreKey(reason.SubspaceID, reason.ID), k.cdc.MustMarshal(&reason)) + + k.Logger(ctx).Debug("reason saved", "subspace id", reason.SubspaceID, "id", reason.ID) + k.AfterReasonSaved(ctx, reason.SubspaceID, reason.ID) +} + +// HasReason tells whether the given reason exists or not +func (k Keeper) HasReason(ctx sdk.Context, subspaceID uint64, reasonID uint32) bool { + store := ctx.KVStore(k.storeKey) + return store.Has(types.ReasonStoreKey(subspaceID, reasonID)) +} + +// GetReason returns the reason associated with the given id. +// If there is no reason with the given id the function will return an empty reason and false. +func (k Keeper) GetReason(ctx sdk.Context, subspaceID uint64, reasonID uint32) (reason types.Reason, found bool) { + store := ctx.KVStore(k.storeKey) + bz := store.Get(types.ReasonStoreKey(subspaceID, reasonID)) + if bz == nil { + return types.Reason{}, false + } + + k.cdc.MustUnmarshal(bz, &reason) + return reason, true +} + +// DeleteReason deletes the reason having the given id from the store +func (k Keeper) DeleteReason(ctx sdk.Context, subspaceID uint64, reasonID uint32) { + reason, found := k.GetReason(ctx, subspaceID, reasonID) + if !found { + return + } + + store := ctx.KVStore(k.storeKey) + store.Delete(types.ReasonStoreKey(subspaceID, reasonID)) + + // Delete all the reports associated to this reason + k.IterateSubspaceReports(ctx, subspaceID, func(report types.Report) (stop bool) { + if types.ContainsReason(report.ReasonsIDs, reasonID) { + k.DeleteReport(ctx, report.SubspaceID, report.ID) + } + return false + }) + + k.Logger(ctx).Debug("reason deleted", "subspace id", reason.SubspaceID, "id", reason.ID) + k.AfterReasonDeleted(ctx, reason.SubspaceID, reason.ID) +} diff --git a/x/reports/keeper/reasons_test.go b/x/reports/keeper/reasons_test.go new file mode 100644 index 0000000000..b7ed200b8e --- /dev/null +++ b/x/reports/keeper/reasons_test.go @@ -0,0 +1,380 @@ +package keeper_test + +import ( + "time" + + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/desmos-labs/desmos/v3/x/reports/types" +) + +func (suite *KeeperTestsuite) TestKeeper_SetNextReasonID() { + testCases := []struct { + name string + store func(ctx sdk.Context) + subspaceID uint64 + reasonID uint32 + check func(ctx sdk.Context) + }{ + { + name: "non existing reason id is set properly", + subspaceID: 1, + reasonID: 1, + check: func(ctx sdk.Context) { + store := ctx.KVStore(suite.storeKey) + stored := types.GetReasonIDFromBytes(store.Get(types.NextReasonIDStoreKey(1))) + suite.Require().Equal(uint32(1), stored) + }, + }, + { + name: "existing reason id is overridden properly", + store: func(ctx sdk.Context) { + store := ctx.KVStore(suite.storeKey) + store.Set(types.NextReasonIDStoreKey(1), types.GetReasonIDBytes(1)) + }, + subspaceID: 1, + reasonID: 2, + check: func(ctx sdk.Context) { + store := ctx.KVStore(suite.storeKey) + stored := types.GetReasonIDFromBytes(store.Get(types.NextReasonIDStoreKey(1))) + suite.Require().Equal(uint32(2), stored) + }, + }, + } + + for _, tc := range testCases { + tc := tc + suite.Run(tc.name, func() { + ctx, _ := suite.ctx.CacheContext() + if tc.store != nil { + tc.store(ctx) + } + + suite.k.SetNextReasonID(ctx, tc.subspaceID, tc.reasonID) + if tc.check != nil { + tc.check(ctx) + } + }) + } +} + +func (suite *KeeperTestsuite) TestKeeper_GetNextReasonID() { + testCases := []struct { + name string + store func(ctx sdk.Context) + subspaceID uint64 + shouldErr bool + expReasonID uint32 + }{ + { + name: "non existing reason id returns error", + subspaceID: 1, + shouldErr: true, + }, + { + name: "existing reason id is returned properly", + store: func(ctx sdk.Context) { + suite.k.SetNextReasonID(ctx, 1, 1) + }, + subspaceID: 1, + shouldErr: false, + expReasonID: 1, + }, + } + + for _, tc := range testCases { + tc := tc + suite.Run(tc.name, func() { + ctx, _ := suite.ctx.CacheContext() + if tc.store != nil { + tc.store(ctx) + } + + reasonID, err := suite.k.GetNextReasonID(ctx, tc.subspaceID) + if tc.shouldErr { + suite.Require().Error(err) + } else { + suite.Require().NoError(err) + suite.Require().Equal(tc.expReasonID, reasonID) + } + }) + } +} + +func (suite *KeeperTestsuite) TestKeeper_DeleteNextReasonID() { + testCases := []struct { + name string + store func(ctx sdk.Context) + subspaceID uint64 + check func(ctx sdk.Context) + }{ + { + name: "non existing reason id is deleted properly", + subspaceID: 1, + check: func(ctx sdk.Context) { + store := ctx.KVStore(suite.storeKey) + suite.Require().False(store.Has(types.NextReasonIDStoreKey(1))) + }, + }, + { + name: "existing reason id is deleted properly", + store: func(ctx sdk.Context) { + suite.k.SetNextReasonID(ctx, 1, 1) + }, + subspaceID: 1, + check: func(ctx sdk.Context) { + store := ctx.KVStore(suite.storeKey) + suite.Require().False(store.Has(types.NextReasonIDStoreKey(1))) + }, + }, + } + + for _, tc := range testCases { + tc := tc + suite.Run(tc.name, func() { + ctx, _ := suite.ctx.CacheContext() + if tc.store != nil { + tc.store(ctx) + } + + suite.k.DeleteNextReasonID(ctx, tc.subspaceID) + if tc.check != nil { + tc.check(ctx) + } + }) + } +} + +// -------------------------------------------------------------------------------------------------------------------- + +func (suite *KeeperTestsuite) TestKeeper_SaveReason() { + testCases := []struct { + name string + store func(ctx sdk.Context) + reason types.Reason + check func(ctx sdk.Context) + }{ + { + name: "non existing reason is stored properly", + reason: types.NewReason( + 1, + 1, + "Spam", + "This content is spam", + ), + check: func(ctx sdk.Context) { + stored, found := suite.k.GetReason(ctx, 1, 1) + suite.Require().True(found) + suite.Require().Equal(types.NewReason( + 1, + 1, + "Spam", + "This content is spam", + ), stored) + }, + }, + { + name: "existing reason is overridden properly", + store: func(ctx sdk.Context) { + suite.k.SaveReason(ctx, types.NewReason( + 1, + 1, + "Spam", + "This content is spam", + )) + }, + reason: types.NewReason( + 1, + 1, + "Self harm", + "This content contains self harm", + ), + check: func(ctx sdk.Context) { + stored, found := suite.k.GetReason(ctx, 1, 1) + suite.Require().True(found) + suite.Require().Equal(types.NewReason( + 1, + 1, + "Self harm", + "This content contains self harm", + ), stored) + }, + }, + } + + for _, tc := range testCases { + tc := tc + suite.Run(tc.name, func() { + ctx, _ := suite.ctx.CacheContext() + if tc.store != nil { + tc.store(ctx) + } + + suite.k.SaveReason(ctx, tc.reason) + if tc.check != nil { + tc.check(ctx) + } + }) + } +} + +func (suite *KeeperTestsuite) TestKeeper_HasReason() { + testCases := []struct { + name string + store func(ctx sdk.Context) + subspaceID uint64 + reasonID uint32 + expResult bool + }{ + { + name: "non existing reason returns false", + subspaceID: 1, + reasonID: 1, + expResult: false, + }, + { + name: "existing reason returns true", + store: func(ctx sdk.Context) { + suite.k.SaveReason(ctx, types.NewReason( + 1, + 1, + "Spam", + "This content is spam", + )) + }, + subspaceID: 1, + reasonID: 1, + expResult: true, + }, + } + + for _, tc := range testCases { + tc := tc + suite.Run(tc.name, func() { + ctx, _ := suite.ctx.CacheContext() + if tc.store != nil { + tc.store(ctx) + } + + result := suite.k.HasReason(ctx, tc.subspaceID, tc.reasonID) + suite.Require().Equal(tc.expResult, result) + }) + } +} + +func (suite *KeeperTestsuite) TestKeeper_GetReason() { + testCases := []struct { + name string + store func(ctx sdk.Context) + subspaceID uint64 + reasonID uint32 + expFound bool + expReason types.Reason + }{ + { + name: "non existing reason returns false and empty reason", + subspaceID: 1, + reasonID: 1, + expFound: false, + expReason: types.Reason{}, + }, + { + name: "existing reason returns true and correct data", + store: func(ctx sdk.Context) { + suite.k.SaveReason(ctx, types.NewReason( + 1, + 1, + "Spam", + "This content is spam", + )) + }, + subspaceID: 1, + reasonID: 1, + expFound: true, + expReason: types.NewReason( + 1, + 1, + "Spam", + "This content is spam", + ), + }, + } + + for _, tc := range testCases { + tc := tc + suite.Run(tc.name, func() { + ctx, _ := suite.ctx.CacheContext() + if tc.store != nil { + tc.store(ctx) + } + + reason, found := suite.k.GetReason(ctx, tc.subspaceID, tc.reasonID) + suite.Require().Equal(tc.expFound, found) + suite.Require().Equal(tc.expReason, reason) + }) + } +} + +func (suite *KeeperTestsuite) TestKeeper_DeleteReason() { + testCases := []struct { + name string + store func(ctx sdk.Context) + subspaceID uint64 + reasonID uint32 + check func(ctx sdk.Context) + }{ + { + name: "non existing reason is deleted properly", + subspaceID: 1, + reasonID: 1, + check: func(ctx sdk.Context) { + suite.Require().False(suite.k.HasReason(ctx, 1, 1)) + }, + }, + { + name: "existing reason is deleted properly", + store: func(ctx sdk.Context) { + suite.k.SaveReason(ctx, types.NewReason( + 1, + 1, + "Spam", + "This content is spam", + )) + + suite.k.SaveReport(ctx, types.NewReport( + 1, + 1, + []uint32{1}, + "This content is spam", + types.NewPostTarget(1), + "cosmos1zkmf50jq4lzvhvp5ekl0sdf2p4g3v9v8edt24z", + time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC), + )) + }, + subspaceID: 1, + reasonID: 1, + check: func(ctx sdk.Context) { + // Make sure the reason is deleted + suite.Require().False(suite.k.HasReason(ctx, 1, 1)) + + // Make sure the associated reports are deleted + suite.Require().False(suite.k.HasReport(ctx, 1, 1)) + }, + }, + } + + for _, tc := range testCases { + tc := tc + suite.Run(tc.name, func() { + ctx, _ := suite.ctx.CacheContext() + if tc.store != nil { + tc.store(ctx) + } + + suite.k.DeleteReason(ctx, tc.subspaceID, tc.reasonID) + if tc.check != nil { + tc.check(ctx) + } + }) + } +} diff --git a/x/reports/keeper/reports.go b/x/reports/keeper/reports.go new file mode 100644 index 0000000000..b959475ec5 --- /dev/null +++ b/x/reports/keeper/reports.go @@ -0,0 +1,159 @@ +package keeper + +import ( + "fmt" + + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + + "github.com/desmos-labs/desmos/v3/x/reports/types" +) + +// SetNextReportID sets the new report id for the given subspace to the store +func (k Keeper) SetNextReportID(ctx sdk.Context, subspaceID uint64, reportID uint64) { + store := ctx.KVStore(k.storeKey) + store.Set(types.NextReportIDStoreKey(subspaceID), types.GetReportIDBytes(reportID)) +} + +// HasNextReportID tells whether a next report id exists for the given subspace +func (k Keeper) HasNextReportID(ctx sdk.Context, subspaceID uint64) bool { + store := ctx.KVStore(k.storeKey) + return store.Has(types.NextReportIDStoreKey(subspaceID)) +} + +// GetNextReportID gets the highest report id for the given subspace +func (k Keeper) GetNextReportID(ctx sdk.Context, subspaceID uint64) (reportID uint64, err error) { + store := ctx.KVStore(k.storeKey) + bz := store.Get(types.NextReportIDStoreKey(subspaceID)) + if bz == nil { + return 0, sdkerrors.Wrapf(types.ErrInvalidGenesis, "initial report id hasn't been set for subspace %d", subspaceID) + } + + reportID = types.GetReportIDFromBytes(bz) + return reportID, nil +} + +// DeleteNextReportID removes the report id key for the given subspace +func (k Keeper) DeleteNextReportID(ctx sdk.Context, subspaceID uint64) { + store := ctx.KVStore(k.storeKey) + store.Delete(types.NextReportIDStoreKey(subspaceID)) +} + +// -------------------------------------------------------------------------------------------------------------------- + +// validateUserReportContent validates the given target data to make sure the reported user has not blocked the reporter +func (k Keeper) validateUserReportContent(ctx sdk.Context, report types.Report, data *types.UserTarget) error { + if k.HasUserBlocked(ctx, data.User, report.Reporter, report.SubspaceID) { + return sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "the reported user has blocked you on subspace %d", report.SubspaceID) + } + + return nil +} + +// validatePostReportContent validates the given post report making sure that: +// - the post exists inside the given subspace +// - the post author has not blocked the reporter +func (k Keeper) validatePostReportContent(ctx sdk.Context, report types.Report, data *types.PostTarget) error { + post, found := k.GetPost(ctx, report.SubspaceID, data.PostID) + if !found { + return sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "post %d does not exist inside subspace %d", data.PostID, report.SubspaceID) + } + + if k.HasUserBlocked(ctx, post.Author, report.Reporter, report.ID) { + return sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "post author has blocked you on this subspace") + } + + return nil +} + +// ValidateReport validates the given report's content +func (k Keeper) ValidateReport(ctx sdk.Context, report types.Report) error { + var err error + switch data := report.Target.GetCachedValue().(type) { + case *types.UserTarget: + err = k.validateUserReportContent(ctx, report, data) + case *types.PostTarget: + err = k.validatePostReportContent(ctx, report, data) + } + + if err != nil { + return err + } + + return report.Validate() +} + +// getContentKey returns the store key used to save the report reference based on its content type +func (k Keeper) getContentKey(subspaceID uint64, target types.ReportTarget, reporter string) []byte { + var contentKey []byte + switch data := target.(type) { + case *types.UserTarget: + contentKey = types.UserReportStoreKey(subspaceID, data.User, reporter) + + case *types.PostTarget: + contentKey = types.PostReportStoreKey(subspaceID, data.PostID, reporter) + } + + if contentKey == nil { + panic(fmt.Errorf("unsupported content type: %T", target)) + } + + return contentKey +} + +// SaveReport saves the given report inside the current context +func (k Keeper) SaveReport(ctx sdk.Context, report types.Report) { + store := ctx.KVStore(k.storeKey) + + // Store the report + store.Set(types.ReportStoreKey(report.SubspaceID, report.ID), k.cdc.MustMarshal(&report)) + + // Set the reference for the content + contentKey := k.getContentKey(report.SubspaceID, report.Target.GetCachedValue().(types.ReportTarget), report.Reporter) + store.Set(contentKey, types.GetReportIDBytes(report.ID)) + + k.Logger(ctx).Debug("report saved", "subspace id", report.SubspaceID, "id", report.ID) + k.AfterReportSaved(ctx, report.SubspaceID, report.ID) +} + +// HasReport tells whether the given report exists or not +func (k Keeper) HasReport(ctx sdk.Context, subspaceID uint64, reportID uint64) bool { + store := ctx.KVStore(k.storeKey) + return store.Has(types.ReportStoreKey(subspaceID, reportID)) +} + +// HasReported tells whether the given reporter has reported the specified target or not +func (k Keeper) HasReported(ctx sdk.Context, subspaceID uint64, reporter string, target types.ReportTarget) bool { + store := ctx.KVStore(k.storeKey) + return store.Has(k.getContentKey(subspaceID, target, reporter)) +} + +// GetReport returns the report associated with the given id. +// If there is no report associated with the given id the function will return an empty report and false. +func (k Keeper) GetReport(ctx sdk.Context, subspaceID uint64, reportID uint64) (report types.Report, found bool) { + store := ctx.KVStore(k.storeKey) + bz := store.Get(types.ReportStoreKey(subspaceID, reportID)) + if bz == nil { + return types.Report{}, false + } + + k.cdc.MustUnmarshal(bz, &report) + return report, true +} + +// DeleteReport deletes the report having the given id from the store +func (k Keeper) DeleteReport(ctx sdk.Context, subspaceID uint64, reportID uint64) { + report, found := k.GetReport(ctx, subspaceID, reportID) + if !found { + return + } + + store := ctx.KVStore(k.storeKey) + + // Delete the report store key + store.Delete(types.ReportStoreKey(report.SubspaceID, report.ID)) + + // Delete the content key + contentKey := k.getContentKey(report.SubspaceID, report.Target.GetCachedValue().(types.ReportTarget), report.Reporter) + store.Delete(contentKey) +} diff --git a/x/reports/keeper/reports_test.go b/x/reports/keeper/reports_test.go new file mode 100644 index 0000000000..c346c988ca --- /dev/null +++ b/x/reports/keeper/reports_test.go @@ -0,0 +1,660 @@ +package keeper_test + +import ( + "time" + + poststypes "github.com/desmos-labs/desmos/v3/x/posts/types" + relationshipstypes "github.com/desmos-labs/desmos/v3/x/relationships/types" + + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/desmos-labs/desmos/v3/x/reports/types" +) + +func (suite *KeeperTestsuite) TestKeeper_SetNextReportID() { + testCases := []struct { + name string + store func(ctx sdk.Context) + subspaceID uint64 + reportID uint64 + check func(ctx sdk.Context) + }{ + { + name: "non existing report id is set properly", + subspaceID: 1, + reportID: 1, + check: func(ctx sdk.Context) { + store := ctx.KVStore(suite.storeKey) + stored := types.GetReportIDFromBytes(store.Get(types.NextReportIDStoreKey(1))) + suite.Require().Equal(uint64(1), stored) + }, + }, + { + name: "existing report id is overridden properly", + store: func(ctx sdk.Context) { + suite.k.SetNextReportID(ctx, 1, 1) + }, + subspaceID: 1, + reportID: 2, + check: func(ctx sdk.Context) { + store := ctx.KVStore(suite.storeKey) + stored := types.GetReportIDFromBytes(store.Get(types.NextReportIDStoreKey(1))) + suite.Require().Equal(uint64(2), stored) + }, + }, + } + + for _, tc := range testCases { + tc := tc + suite.Run(tc.name, func() { + ctx, _ := suite.ctx.CacheContext() + if tc.store != nil { + tc.store(ctx) + } + + suite.k.SetNextReportID(ctx, tc.subspaceID, tc.reportID) + if tc.check != nil { + tc.check(ctx) + } + }) + } +} + +func (suite *KeeperTestsuite) TestKeeper_GetNextReportID() { + testCases := []struct { + name string + store func(ctx sdk.Context) + subspaceID uint64 + shouldErr bool + expReportID uint64 + }{ + { + name: "non existing report id returns error", + subspaceID: 1, + shouldErr: true, + }, + { + name: "existing report id is returned properly", + store: func(ctx sdk.Context) { + suite.k.SetNextReportID(ctx, 1, 1) + }, + subspaceID: 1, + shouldErr: false, + expReportID: 1, + }, + } + + for _, tc := range testCases { + tc := tc + suite.Run(tc.name, func() { + ctx, _ := suite.ctx.CacheContext() + if tc.store != nil { + tc.store(ctx) + } + + reportID, err := suite.k.GetNextReportID(ctx, tc.subspaceID) + if tc.shouldErr { + suite.Require().Error(err) + } else { + suite.Require().NoError(err) + suite.Require().Equal(tc.expReportID, reportID) + } + }) + } +} + +func (suite *KeeperTestsuite) TestKeeper_DeleteNextReportID() { + testCases := []struct { + name string + store func(ctx sdk.Context) + subspaceID uint64 + check func(ctx sdk.Context) + }{ + { + name: "existing report id is deleted properly", + store: func(ctx sdk.Context) { + suite.k.SetNextReportID(ctx, 1, 1) + }, + subspaceID: 1, + check: func(ctx sdk.Context) { + store := ctx.KVStore(suite.storeKey) + suite.Require().False(store.Has(types.NextReportIDStoreKey(1))) + }, + }, + } + + for _, tc := range testCases { + tc := tc + suite.Run(tc.name, func() { + ctx, _ := suite.ctx.CacheContext() + if tc.store != nil { + tc.store(ctx) + } + + suite.k.DeleteNextReportID(ctx, tc.subspaceID) + if tc.check != nil { + tc.check(ctx) + } + }) + } +} + +// -------------------------------------------------------------------------------------------------------------------- + +func (suite *KeeperTestsuite) TestKeeper_ValidateReport() { + testCases := []struct { + name string + store func(ctx sdk.Context) + report types.Report + shouldErr bool + }{ + { + name: "UserTarget - blocked reporter returns error", + store: func(ctx sdk.Context) { + suite.rk.SaveUserBlock(ctx, relationshipstypes.NewUserBlock( + "cosmos10s22qjua2n3law0ymstm3txm7764mfk2cjawq5", + "cosmos1wprgptc8ktt0eemrn2znpxv8crdxm8tdpkdr7w", + "", + 1, + )) + }, + report: types.NewReport( + 1, + 1, + []uint32{1}, + "This content is spam", + types.NewUserTarget("cosmos10s22qjua2n3law0ymstm3txm7764mfk2cjawq5"), + "cosmos1wprgptc8ktt0eemrn2znpxv8crdxm8tdpkdr7w", + time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC), + ), + shouldErr: true, + }, + { + name: "UserTarget - valid data returns no error", + report: types.NewReport( + 1, + 1, + []uint32{1}, + "This content is spam", + types.NewUserTarget("cosmos10s22qjua2n3law0ymstm3txm7764mfk2cjawq5"), + "cosmos1wprgptc8ktt0eemrn2znpxv8crdxm8tdpkdr7w", + time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC), + ), + shouldErr: false, + }, + { + name: "PostTarget - not found post returns error", + report: types.NewReport( + 1, + 1, + []uint32{1}, + "This user is spamming", + types.NewPostTarget(1), + "cosmos1ggzk8tnte9lmzgpvyzzdtmwmn6rjlct4spmjjd", + time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC), + ), + shouldErr: true, + }, + { + name: "PostTarget - blocked user returns error", + store: func(ctx sdk.Context) { + suite.pk.SavePost(ctx, poststypes.NewPost( + 1, + 0, + 1, + "", + "This is a new post", + "cosmos10s22qjua2n3law0ymstm3txm7764mfk2cjawq5", + 0, + nil, + nil, + poststypes.REPLY_SETTING_EVERYONE, + time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC), + nil, + )) + + suite.rk.SaveUserBlock(ctx, relationshipstypes.NewUserBlock( + "cosmos10s22qjua2n3law0ymstm3txm7764mfk2cjawq5", + "cosmos1wprgptc8ktt0eemrn2znpxv8crdxm8tdpkdr7w", + "", + 1, + )) + }, + report: types.NewReport( + 1, + 1, + []uint32{1}, + "This user is spamming", + types.NewPostTarget(1), + "cosmos1wprgptc8ktt0eemrn2znpxv8crdxm8tdpkdr7w", + time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC), + ), + shouldErr: true, + }, + { + name: "PostsData - valid data returns no error", + store: func(ctx sdk.Context) { + suite.pk.SavePost(ctx, poststypes.NewPost( + 1, + 0, + 1, + "", + "This is a new post", + "cosmos1r9jamre0x0qqy562rhhckt6sryztwhnvhafyz4", + 0, + nil, + nil, + poststypes.REPLY_SETTING_EVERYONE, + time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC), + nil, + )) + }, + report: types.NewReport( + 1, + 1, + []uint32{1}, + "This user is spamming", + types.NewPostTarget(1), + "cosmos1wprgptc8ktt0eemrn2znpxv8crdxm8tdpkdr7w", + time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC), + ), + shouldErr: false, + }, + } + + for _, tc := range testCases { + tc := tc + suite.Run(tc.name, func() { + ctx, _ := suite.ctx.CacheContext() + if tc.store != nil { + tc.store(ctx) + } + + err := suite.k.ValidateReport(ctx, tc.report) + if tc.shouldErr { + suite.Require().Error(err) + } else { + suite.Require().NoError(err) + } + }) + } +} + +func (suite *KeeperTestsuite) TestKeeper_SaveReport() { + testCases := []struct { + name string + store func(ctx sdk.Context) + report types.Report + check func(ctx sdk.Context) + }{ + { + name: "post report is stored properly", + report: types.NewReport( + 1, + 1, + []uint32{1}, + "This content is spam", + types.NewPostTarget(1), + "cosmos1zkmf50jq4lzvhvp5ekl0sdf2p4g3v9v8edt24z", + time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC), + ), + check: func(ctx sdk.Context) { + stored, found := suite.k.GetReport(ctx, 1, 1) + suite.Require().True(found) + suite.Require().Equal(types.NewReport( + 1, + 1, + []uint32{1}, + "This content is spam", + types.NewPostTarget(1), + "cosmos1zkmf50jq4lzvhvp5ekl0sdf2p4g3v9v8edt24z", + time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC), + ), stored) + + // Check the content key + store := ctx.KVStore(suite.storeKey) + suite.Require().True(store.Has(types.PostReportStoreKey(1, 1, "cosmos1zkmf50jq4lzvhvp5ekl0sdf2p4g3v9v8edt24z"))) + }, + }, + { + name: "user report is stored properly", + report: types.NewReport( + 1, + 1, + []uint32{1}, + "This content is spam", + types.NewUserTarget("cosmos1pjffdtweghpyxru9alssyqtdkq8mn6sepgstgm"), + "cosmos1zkmf50jq4lzvhvp5ekl0sdf2p4g3v9v8edt24z", + time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC), + ), + check: func(ctx sdk.Context) { + stored, found := suite.k.GetReport(ctx, 1, 1) + suite.Require().True(found) + suite.Require().Equal(types.NewReport( + 1, + 1, + []uint32{1}, + "This content is spam", + types.NewUserTarget("cosmos1pjffdtweghpyxru9alssyqtdkq8mn6sepgstgm"), + "cosmos1zkmf50jq4lzvhvp5ekl0sdf2p4g3v9v8edt24z", + time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC), + ), stored) + + // Check the content key + store := ctx.KVStore(suite.storeKey) + suite.Require().True(store.Has(types.UserReportStoreKey(1, "cosmos1pjffdtweghpyxru9alssyqtdkq8mn6sepgstgm", "cosmos1zkmf50jq4lzvhvp5ekl0sdf2p4g3v9v8edt24z"))) + }, + }, + } + + for _, tc := range testCases { + tc := tc + suite.Run(tc.name, func() { + ctx, _ := suite.ctx.CacheContext() + if tc.store != nil { + tc.store(ctx) + } + + suite.k.SaveReport(ctx, tc.report) + if tc.check != nil { + tc.check(ctx) + } + }) + } +} + +func (suite *KeeperTestsuite) TestKeeper_HasReport() { + testCases := []struct { + name string + store func(ctx sdk.Context) + subspaceID uint64 + reportID uint64 + expResult bool + }{ + { + name: "non existing report returns false", + subspaceID: 1, + reportID: 1, + expResult: false, + }, + { + name: "existing report returns true", + store: func(ctx sdk.Context) { + suite.k.SaveReport(ctx, types.NewReport( + 1, + 1, + []uint32{1}, + "This content is spam", + types.NewPostTarget(1), + "cosmos1zkmf50jq4lzvhvp5ekl0sdf2p4g3v9v8edt24z", + time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC), + )) + }, + subspaceID: 1, + reportID: 1, + expResult: true, + }, + } + + for _, tc := range testCases { + tc := tc + suite.Run(tc.name, func() { + ctx, _ := suite.ctx.CacheContext() + if tc.store != nil { + tc.store(ctx) + } + + result := suite.k.HasReport(ctx, tc.subspaceID, tc.reportID) + suite.Require().Equal(tc.expResult, result) + }) + } +} + +func (suite *KeeperTestsuite) TestKeeper_HasReported() { + testCases := []struct { + name string + store func(ctx sdk.Context) + subspaceID uint64 + reporter string + target types.ReportTarget + expResult bool + }{ + { + name: "not reported target returns false - different post id", + store: func(ctx sdk.Context) { + suite.k.SaveReport(ctx, types.NewReport( + 1, + 1, + []uint32{1}, + "", + types.NewPostTarget(2), + "cosmos1qqjdwjjxxgfpk9kvn0a6gpxmzgvd2z0jtd72e4", + time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC), + )) + }, + subspaceID: 1, + reporter: "cosmos1qqjdwjjxxgfpk9kvn0a6gpxmzgvd2z0jtd72e4", + target: types.NewPostTarget(1), + expResult: false, + }, + { + name: "not reported target returns false - different user address", + store: func(ctx sdk.Context) { + suite.k.SaveReport(ctx, types.NewReport( + 1, + 1, + []uint32{1}, + "", + types.NewUserTarget("cosmos1dzwwn72sevnakh4qhhpzmsqlsj3ehzf9n803yh"), + "cosmos1qqjdwjjxxgfpk9kvn0a6gpxmzgvd2z0jtd72e4", + time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC), + )) + }, + subspaceID: 1, + reporter: "cosmos1dzwwn72sevnakh4qhhpzmsqlsj3ehzf9n803yh", + target: types.NewUserTarget("cosmos14uhwtt50cge4mywlr8897tef78gkjg75ugc9rq"), + expResult: false, + }, + { + name: "not reported target returns false - different subspace id", + store: func(ctx sdk.Context) { + suite.k.SaveReport(ctx, types.NewReport( + 2, + 1, + []uint32{1}, + "", + types.NewPostTarget(1), + "cosmos1qqjdwjjxxgfpk9kvn0a6gpxmzgvd2z0jtd72e4", + time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC), + )) + }, + subspaceID: 1, + reporter: "cosmos1qqjdwjjxxgfpk9kvn0a6gpxmzgvd2z0jtd72e4", + target: types.NewPostTarget(1), + expResult: false, + }, + { + name: "not reported target returns false - different reporter", + store: func(ctx sdk.Context) { + suite.k.SaveReport(ctx, types.NewReport( + 1, + 1, + []uint32{1}, + "", + types.NewPostTarget(1), + "cosmos1hjvrc2rvy0jenpfquk536755x4cgvjqhqj6t3d", + time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC), + )) + }, + subspaceID: 1, + reporter: "cosmos1qqjdwjjxxgfpk9kvn0a6gpxmzgvd2z0jtd72e4", + target: types.NewPostTarget(1), + expResult: false, + }, + { + name: "reported post returns true", + store: func(ctx sdk.Context) { + suite.k.SaveReport(ctx, types.NewReport( + 1, + 1, + []uint32{1}, + "", + types.NewPostTarget(1), + "cosmos1qqjdwjjxxgfpk9kvn0a6gpxmzgvd2z0jtd72e4", + time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC), + )) + }, + subspaceID: 1, + reporter: "cosmos1qqjdwjjxxgfpk9kvn0a6gpxmzgvd2z0jtd72e4", + target: types.NewPostTarget(1), + expResult: true, + }, + { + name: "reported user returns true", + store: func(ctx sdk.Context) { + suite.k.SaveReport(ctx, types.NewReport( + 1, + 1, + []uint32{1}, + "", + types.NewUserTarget("cosmos14uhwtt50cge4mywlr8897tef78gkjg75ugc9rq"), + "cosmos1qqjdwjjxxgfpk9kvn0a6gpxmzgvd2z0jtd72e4", + time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC), + )) + }, + subspaceID: 1, + reporter: "cosmos1qqjdwjjxxgfpk9kvn0a6gpxmzgvd2z0jtd72e4", + target: types.NewUserTarget("cosmos14uhwtt50cge4mywlr8897tef78gkjg75ugc9rq"), + expResult: true, + }, + } + + for _, tc := range testCases { + tc := tc + suite.Run(tc.name, func() { + ctx, _ := suite.ctx.CacheContext() + if tc.store != nil { + tc.store(ctx) + } + + result := suite.k.HasReported(ctx, tc.subspaceID, tc.reporter, tc.target) + suite.Require().Equal(tc.expResult, result) + }) + } +} + +func (suite *KeeperTestsuite) TestKeeper_GetReport() { + testCases := []struct { + name string + store func(ctx sdk.Context) + subspaceID uint64 + reportID uint64 + expFound bool + expReport types.Report + }{ + { + name: "non existing report returns false and empty report", + subspaceID: 1, + reportID: 1, + expFound: false, + expReport: types.Report{}, + }, + { + name: "existing report returns true and correct data", + store: func(ctx sdk.Context) { + suite.k.SaveReport(ctx, types.NewReport( + 1, + 1, + []uint32{1}, + "This content is spam", + types.NewPostTarget(1), + "cosmos1zkmf50jq4lzvhvp5ekl0sdf2p4g3v9v8edt24z", + time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC), + )) + }, + subspaceID: 1, + reportID: 1, + expFound: true, + expReport: types.NewReport( + 1, + 1, + []uint32{1}, + "This content is spam", + types.NewPostTarget(1), + "cosmos1zkmf50jq4lzvhvp5ekl0sdf2p4g3v9v8edt24z", + time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC), + ), + }, + } + + for _, tc := range testCases { + tc := tc + suite.Run(tc.name, func() { + ctx, _ := suite.ctx.CacheContext() + if tc.store != nil { + tc.store(ctx) + } + + report, found := suite.k.GetReport(ctx, tc.subspaceID, tc.reportID) + suite.Require().Equal(tc.expFound, found) + suite.Require().Equal(tc.expReport, report) + }) + } +} + +func (suite *KeeperTestsuite) TestKeeper_DeleteReport() { + testCases := []struct { + name string + store func(ctx sdk.Context) + subspaceID uint64 + reportID uint64 + check func(ctx sdk.Context) + }{ + { + name: "non existing report is deleted properly", + subspaceID: 1, + reportID: 1, + check: func(ctx sdk.Context) { + suite.Require().False(suite.k.HasReport(ctx, 1, 1)) + }, + }, + { + name: "existing report is deleted properly", + store: func(ctx sdk.Context) { + suite.k.SaveReport(ctx, types.NewReport( + 1, + 1, + []uint32{1}, + "This content is spam", + types.NewPostTarget(1), + "cosmos1zkmf50jq4lzvhvp5ekl0sdf2p4g3v9v8edt24z", + time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC), + )) + }, + subspaceID: 1, + reportID: 1, + check: func(ctx sdk.Context) { + suite.Require().False(suite.k.HasReport(ctx, 1, 1)) + + // Check the content key + store := ctx.KVStore(suite.storeKey) + suite.Require().False(store.Has(types.PostReportStoreKey(1, 1, "cosmos1zkmf50jq4lzvhvp5ekl0sdf2p4g3v9v8edt24z"))) + }, + }, + } + + for _, tc := range testCases { + tc := tc + suite.Run(tc.name, func() { + ctx, _ := suite.ctx.CacheContext() + if tc.store != nil { + tc.store(ctx) + } + + suite.k.DeleteReport(ctx, tc.subspaceID, tc.reportID) + if tc.check != nil { + tc.check(ctx) + } + }) + } +} diff --git a/x/reports/legacy/v2/store.go b/x/reports/legacy/v2/store.go new file mode 100644 index 0000000000..0b02121a3d --- /dev/null +++ b/x/reports/legacy/v2/store.go @@ -0,0 +1,27 @@ +package v2 + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/desmos-labs/desmos/v3/x/reports/types" + subspacestypes "github.com/desmos-labs/desmos/v3/x/subspaces/types" +) + +type SubspacesKeeper interface { + IterateSubspaces(ctx sdk.Context, fn func(subspaces subspacestypes.Subspace) (stop bool)) +} + +// MigrateStore performs in-place store migrations from v1 to v2 +// The only thing that is done here is setting up the next reason id and report id keys for existing subspaces. +func MigrateStore(ctx sdk.Context, storeKey sdk.StoreKey, sk SubspacesKeeper) error { + store := ctx.KVStore(storeKey) + + // Set the next reason id and report id for all the subspaces + sk.IterateSubspaces(ctx, func(subspace subspacestypes.Subspace) (stop bool) { + store.Set(types.NextReasonIDStoreKey(subspace.ID), types.GetReasonIDBytes(1)) + store.Set(types.NextReportIDStoreKey(subspace.ID), types.GetReportIDBytes(1)) + return false + }) + + return nil +} diff --git a/x/reports/legacy/v2/store_test.go b/x/reports/legacy/v2/store_test.go new file mode 100644 index 0000000000..31c7f67a01 --- /dev/null +++ b/x/reports/legacy/v2/store_test.go @@ -0,0 +1,91 @@ +package v2_test + +import ( + "testing" + "time" + + sdk "github.com/cosmos/cosmos-sdk/types" + capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" + paramstypes "github.com/cosmos/cosmos-sdk/x/params/types" + "github.com/stretchr/testify/require" + + "github.com/desmos-labs/desmos/v3/app" + "github.com/desmos-labs/desmos/v3/testutil" + v2 "github.com/desmos-labs/desmos/v3/x/reports/legacy/v2" + "github.com/desmos-labs/desmos/v3/x/reports/types" + subspaceskeeper "github.com/desmos-labs/desmos/v3/x/subspaces/keeper" + subspacestypes "github.com/desmos-labs/desmos/v3/x/subspaces/types" +) + +func TestMigrateStore(t *testing.T) { + cdc, _ := app.MakeCodecs() + + // Build all the necessary keys + keys := sdk.NewKVStoreKeys(subspacestypes.StoreKey, types.StoreKey) + tKeys := sdk.NewTransientStoreKeys(paramstypes.TStoreKey) + memKeys := sdk.NewMemoryStoreKeys(capabilitytypes.MemStoreKey) + + sk := subspaceskeeper.NewKeeper(cdc, keys[subspacestypes.StoreKey]) + + testCases := []struct { + name string + store func(ctx sdk.Context) + shouldErr bool + check func(ctx sdk.Context) + }{ + { + name: "next report and reason ids are set for existing subspaces", + store: func(ctx sdk.Context) { + sk.SaveSubspace(ctx, subspacestypes.NewSubspace( + 1, + "This is a test subspace", + "This is a test subspace", + "cosmos1s0he0z3g92zwsxdj83h0ky9w463sx7gq9mqtgn", + "cosmos1s0he0z3g92zwsxdj83h0ky9w463sx7gq9mqtgn", + "cosmos1s0he0z3g92zwsxdj83h0ky9w463sx7gq9mqtgn", + time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC), + )) + + sk.SaveSubspace(ctx, subspacestypes.NewSubspace( + 2, + "This is another test subspace", + "This is anoter test subspace", + "cosmos1s0he0z3g92zwsxdj83h0ky9w463sx7gq9mqtgn", + "cosmos1s0he0z3g92zwsxdj83h0ky9w463sx7gq9mqtgn", + "cosmos1s0he0z3g92zwsxdj83h0ky9w463sx7gq9mqtgn", + time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC), + )) + }, + shouldErr: false, + check: func(ctx sdk.Context) { + store := ctx.KVStore(keys[types.StoreKey]) + + require.Equal(t, uint32(1), types.GetReasonIDFromBytes(store.Get(types.NextReasonIDStoreKey(1)))) + require.Equal(t, uint64(1), types.GetReportIDFromBytes(store.Get(types.NextReportIDStoreKey(1)))) + + require.Equal(t, uint32(1), types.GetReasonIDFromBytes(store.Get(types.NextReasonIDStoreKey(2)))) + require.Equal(t, uint64(1), types.GetReportIDFromBytes(store.Get(types.NextReportIDStoreKey(2)))) + }, + }, + } + + for _, tc := range testCases { + tc := tc + t.Run(tc.name, func(t *testing.T) { + ctx := testutil.BuildContext(keys, tKeys, memKeys) + if tc.store != nil { + tc.store(ctx) + } + + err := v2.MigrateStore(ctx, keys[types.StoreKey], sk) + if tc.shouldErr { + require.Error(t, err) + } else { + require.NoError(t, err) + if tc.check != nil { + tc.check(ctx) + } + } + }) + } +} diff --git a/x/reports/module.go b/x/reports/module.go new file mode 100644 index 0000000000..b619bfb3d7 --- /dev/null +++ b/x/reports/module.go @@ -0,0 +1,223 @@ +package reports + +import ( + "context" + "encoding/json" + "fmt" + "math/rand" + + subspaceskeeper "github.com/desmos-labs/desmos/v3/x/subspaces/keeper" + + feeskeeper "github.com/desmos-labs/desmos/v3/x/fees/keeper" + postskeeper "github.com/desmos-labs/desmos/v3/x/posts/keeper" + + "github.com/desmos-labs/desmos/v3/x/reports/simulation" + + "github.com/desmos-labs/desmos/v3/x/reports/client/cli" + + "github.com/desmos-labs/desmos/v3/x/reports/keeper" + "github.com/desmos-labs/desmos/v3/x/reports/types" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/codec" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/module" + simtypes "github.com/cosmos/cosmos-sdk/types/simulation" + authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper" + bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper" + "github.com/gorilla/mux" + "github.com/grpc-ecosystem/grpc-gateway/runtime" + "github.com/spf13/cobra" + abci "github.com/tendermint/tendermint/abci/types" +) + +const ( + consensusVersion = 2 +) + +// type check to ensure the interface is properly implemented +var ( + _ module.AppModule = AppModule{} + _ module.AppModuleBasic = AppModuleBasic{} + _ module.AppModuleSimulation = AppModule{} +) + +// AppModuleBasic defines the basic application module used by the reports module. +type AppModuleBasic struct { + cdc codec.Codec +} + +// Name returns the reports module's name. +func (AppModuleBasic) Name() string { + return types.ModuleName +} + +// RegisterLegacyAminoCodec registers the reports module's types for the given codec. +func (AppModuleBasic) RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) { + types.RegisterLegacyAminoCodec(cdc) +} + +// DefaultGenesis returns default genesis state as raw bytes for the reports module. +func (AppModuleBasic) DefaultGenesis(cdc codec.JSONCodec) json.RawMessage { + return cdc.MustMarshalJSON(types.DefaultGenesisState()) +} + +// ValidateGenesis performs genesis state validation for the reports module. +func (AppModuleBasic) ValidateGenesis(cdc codec.JSONCodec, _ client.TxEncodingConfig, bz json.RawMessage) error { + var data types.GenesisState + if err := cdc.UnmarshalJSON(bz, &data); err != nil { + return fmt.Errorf("failed to unmarshal %s genesis state: %w", types.ModuleName, err) + } + return types.ValidateGenesis(&data) +} + +// RegisterRESTRoutes registers the REST routes for the reports module. +func (AppModuleBasic) RegisterRESTRoutes(_ client.Context, _ *mux.Router) {} + +// RegisterGRPCGatewayRoutes registers the gRPC Gateway routes for the reports module. +func (AppModuleBasic) RegisterGRPCGatewayRoutes(clientCtx client.Context, mux *runtime.ServeMux) { + types.RegisterQueryHandlerClient(context.Background(), mux, types.NewQueryClient(clientCtx)) +} + +// GetTxCmd returns the root tx command for the reports module. +func (AppModuleBasic) GetTxCmd() *cobra.Command { + return cli.NewTxCmd() +} + +// GetQueryCmd returns the root query command for the reports module. +func (AppModuleBasic) GetQueryCmd() *cobra.Command { + return cli.GetQueryCmd() +} + +// RegisterInterfaces registers interfaces and implementations of the reports module. +func (AppModuleBasic) RegisterInterfaces(registry codectypes.InterfaceRegistry) { + types.RegisterInterfaces(registry) +} + +// -------------------------------------------------------------------------------------------------------------------- + +// AppModule implements an application module for the reports module. +type AppModule struct { + AppModuleBasic + keeper keeper.Keeper + sk subspaceskeeper.Keeper + pk postskeeper.Keeper + ak authkeeper.AccountKeeper + bk bankkeeper.Keeper + fk feeskeeper.Keeper +} + +// RegisterServices registers module services. +func (am AppModule) RegisterServices(cfg module.Configurator) { + types.RegisterMsgServer(cfg.MsgServer(), keeper.NewMsgServerImpl(am.keeper)) + types.RegisterQueryServer(cfg.QueryServer(), am.keeper) + + m := keeper.NewMigrator(am.keeper, am.sk) + err := cfg.RegisterMigration(types.ModuleName, 1, m.Migrate1to2) + if err != nil { + panic(err) + } +} + +// NewAppModule creates a new AppModule Object +func NewAppModule( + cdc codec.Codec, keeper keeper.Keeper, sk subspaceskeeper.Keeper, pk postskeeper.Keeper, + ak authkeeper.AccountKeeper, bk bankkeeper.Keeper, fk feeskeeper.Keeper, +) AppModule { + return AppModule{ + AppModuleBasic: AppModuleBasic{cdc: cdc}, + keeper: keeper, + sk: sk, + pk: pk, + ak: ak, + bk: bk, + fk: fk, + } +} + +// Name returns the reports module's name. +func (AppModule) Name() string { + return types.ModuleName +} + +// RegisterInvariants performs a no-op. +func (am AppModule) RegisterInvariants(ir sdk.InvariantRegistry) { + keeper.RegisterInvariants(ir, am.keeper) +} + +// Deprecated: Route returns the module's message router and handler. +func (am AppModule) Route() sdk.Route { + return sdk.Route{} +} + +// QuerierRoute returns the reports module's querier route name. +func (am AppModule) QuerierRoute() string { + return types.QuerierRoute +} + +// LegacyQuerierHandler returns the reports module sdk.Querier. +func (am AppModule) LegacyQuerierHandler(_ *codec.LegacyAmino) sdk.Querier { + return nil +} + +// InitGenesis performs genesis initialization for the reports module. +// It returns no validator updates. +func (am AppModule) InitGenesis(ctx sdk.Context, cdc codec.JSONCodec, data json.RawMessage) []abci.ValidatorUpdate { + var genesisState types.GenesisState + cdc.MustUnmarshalJSON(data, &genesisState) + am.keeper.InitGenesis(ctx, genesisState) + return []abci.ValidatorUpdate{} +} + +// ExportGenesis returns the exported genesis state as raw bytes for the +// reports module. +func (am AppModule) ExportGenesis(ctx sdk.Context, cdc codec.JSONCodec) json.RawMessage { + gs := am.keeper.ExportGenesis(ctx) + return cdc.MustMarshalJSON(gs) +} + +// ConsensusVersion implements AppModule. +func (AppModule) ConsensusVersion() uint64 { + return consensusVersion +} + +// BeginBlock returns the begin blocker for the reports module. +func (am AppModule) BeginBlock(_ sdk.Context, _ abci.RequestBeginBlock) { +} + +// EndBlock returns the end blocker for the reports module. It returns no validator +// updates. +func (am AppModule) EndBlock(_ sdk.Context, _ abci.RequestEndBlock) []abci.ValidatorUpdate { + return []abci.ValidatorUpdate{} +} + +// -------------------------------------------------------------------------------------------------------------------- + +// AppModuleSimulation defines the module simulation functions used by the reports module. +type AppModuleSimulation struct{} + +// GenerateGenesisState creates a randomized GenState of the bank module. +func (AppModule) GenerateGenesisState(simState *module.SimulationState) { + simulation.RandomizeGenState(simState) +} + +// ProposalContents doesn't return any content functions for governance proposals. +func (AppModule) ProposalContents(_ module.SimulationState) []simtypes.WeightedProposalContent { + return nil +} + +// RandomizedParams creates randomized reports param changes for the simulator. +func (AppModule) RandomizedParams(r *rand.Rand) []simtypes.ParamChange { + return simulation.ParamChanges(r) +} + +// RegisterStoreDecoder performs a no-op. +func (am AppModule) RegisterStoreDecoder(sdr sdk.StoreDecoderRegistry) { + sdr[types.ModuleName] = simulation.NewDecodeStore(am.cdc) +} + +// WeightedOperations returns the all the reports module operations with their respective weights. +func (am AppModule) WeightedOperations(simState module.SimulationState) []simtypes.WeightedOperation { + return simulation.WeightedOperations(simState.AppParams, simState.Cdc, am.keeper, am.sk, am.pk, am.ak, am.bk, am.fk) +} diff --git a/x/reports/simulation/decoder.go b/x/reports/simulation/decoder.go new file mode 100644 index 0000000000..56baf94b22 --- /dev/null +++ b/x/reports/simulation/decoder.go @@ -0,0 +1,58 @@ +package simulation + +import ( + "bytes" + "fmt" + + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/types/kv" + + "github.com/desmos-labs/desmos/v3/x/reports/types" +) + +// NewDecodeStore returns a new decoder that unmarshals the KVPair's Value +// to the corresponding reports type +func NewDecodeStore(cdc codec.Codec) func(kvA, kvB kv.Pair) string { + return func(kvA, kvB kv.Pair) string { + switch { + case bytes.HasPrefix(kvA.Key, types.NextReportIDPrefix): + var idA, idB uint64 + idA = types.GetReportIDFromBytes(kvA.Value) + idB = types.GetReportIDFromBytes(kvB.Value) + return fmt.Sprintf("NextReportIDA: %d\nNextReportIDB: %d\n", idA, idB) + + case bytes.HasPrefix(kvA.Key, types.ReportPrefix): + var reportA, reportB types.Report + cdc.MustUnmarshal(kvA.Value, &reportA) + cdc.MustUnmarshal(kvB.Value, &reportB) + return fmt.Sprintf("ReportA: %s\nReportB: %s\n", &reportA, &reportB) + + case bytes.HasPrefix(kvA.Key, types.PostsReportsPrefix): + var idA, idB uint64 + idA = types.GetReportIDFromBytes(kvA.Value) + idB = types.GetReportIDFromBytes(kvB.Value) + return fmt.Sprintf("PostReportIDA: %d\nPostReportIDB: %d\n", idA, idB) + + case bytes.HasPrefix(kvA.Key, types.UsersReportsPrefix): + var idA, idB uint64 + idA = types.GetReportIDFromBytes(kvA.Value) + idB = types.GetReportIDFromBytes(kvB.Value) + return fmt.Sprintf("UserReportIDA: %d\nUserReportIDB: %d\n", idA, idB) + + case bytes.HasPrefix(kvA.Key, types.NextReasonIDPrefix): + var idA, idB uint32 + idA = types.GetReasonIDFromBytes(kvA.Value) + idB = types.GetReasonIDFromBytes(kvB.Value) + return fmt.Sprintf("NextReasonIDA: %d\nNextReasonIDB: %d\n", idA, idB) + + case bytes.HasPrefix(kvA.Key, types.ReasonPrefix): + var reasonA, reasonB types.Reason + cdc.MustUnmarshal(kvA.Value, &reasonA) + cdc.MustUnmarshal(kvB.Value, &reasonB) + return fmt.Sprintf("ReasonA: %s\nReasonB: %s\n", &reasonA, &reasonB) + + default: + panic(fmt.Sprintf("unexpected %s key %X (%s)", types.ModuleName, kvA.Key, kvA.Key)) + } + } +} diff --git a/x/reports/simulation/decoder_test.go b/x/reports/simulation/decoder_test.go new file mode 100644 index 0000000000..95fbb6ff69 --- /dev/null +++ b/x/reports/simulation/decoder_test.go @@ -0,0 +1,98 @@ +package simulation_test + +import ( + "fmt" + "testing" + "time" + + "github.com/desmos-labs/desmos/v3/x/reports/simulation" + + "github.com/cosmos/cosmos-sdk/types/kv" + "github.com/stretchr/testify/require" + + "github.com/desmos-labs/desmos/v3/app" + "github.com/desmos-labs/desmos/v3/x/reports/types" +) + +func TestDecodeStore(t *testing.T) { + cdc, _ := app.MakeCodecs() + decoder := simulation.NewDecodeStore(cdc) + + report := types.NewReport( + 1, + 1, + []uint32{1}, + "This user is spamming", + types.NewUserTarget("cosmos1nv9kkuads7f627q2zf4k9kwdudx709rjck3s7e"), + "cosmos1z0glns8fv5h0xgghg4nkq0jjy9gp0l682tcf79", + time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC), + ) + reason := types.NewReason( + 1, + 1, + "Spam", + "This content is spam", + ) + + kvPairs := kv.Pairs{Pairs: []kv.Pair{ + { + Key: types.NextReportIDStoreKey(1), + Value: types.GetReportIDBytes(1), + }, + { + Key: types.ReportStoreKey(1, 1), + Value: cdc.MustMarshal(&report), + }, + { + Key: types.PostReportStoreKey(1, 1, "cosmos1z0glns8fv5h0xgghg4nkq0jjy9gp0l682tcf79"), + Value: types.GetReportIDBytes(1), + }, + { + Key: types.UserReportStoreKey(1, "cosmos1nv9kkuads7f627q2zf4k9kwdudx709rjck3s7e", "cosmos1z0glns8fv5h0xgghg4nkq0jjy9gp0l682tcf79"), + Value: types.GetReportIDBytes(1), + }, + { + Key: types.NextReasonIDStoreKey(1), + Value: types.GetReasonIDBytes(1), + }, + { + Key: types.ReasonStoreKey(1, 1), + Value: cdc.MustMarshal(&reason), + }, + { + Key: []byte("Unknown key"), + Value: nil, + }, + }} + + testCases := []struct { + name string + expectedLog string + }{ + {"Next Report ID", fmt.Sprintf("NextReportIDA: %d\nNextReportIDB: %d\n", + 1, 1)}, + {"Report", fmt.Sprintf("ReportA: %s\nReportB: %s\n", + &report, &report)}, + {"Post Report ID", fmt.Sprintf("PostReportIDA: %d\nPostReportIDB: %d\n", + 1, 1)}, + {"User Report ID", fmt.Sprintf("UserReportIDA: %d\nUserReportIDB: %d\n", + 1, 1)}, + {"Next Reason ID", fmt.Sprintf("NextReasonIDA: %d\nNextReasonIDB: %d\n", + 1, 1)}, + {"Reason", fmt.Sprintf("ReasonA: %s\nReasonB: %s\n", + &reason, &reason)}, + {"other", ""}, + } + + for i, tc := range testCases { + i, tc := i, tc + t.Run(tc.name, func(t *testing.T) { + switch i { + case len(testCases) - 1: + require.Panics(t, func() { decoder(kvPairs.Pairs[i], kvPairs.Pairs[i]) }, tc.name) + default: + require.Equal(t, tc.expectedLog, decoder(kvPairs.Pairs[i], kvPairs.Pairs[i]), tc.name) + } + }) + } +} diff --git a/x/reports/simulation/genesis.go b/x/reports/simulation/genesis.go new file mode 100644 index 0000000000..40a0eeaf25 --- /dev/null +++ b/x/reports/simulation/genesis.go @@ -0,0 +1,187 @@ +package simulation + +// DONTCOVER + +import ( + "math/rand" + "time" + + relationshipstypes "github.com/desmos-labs/desmos/v3/x/relationships/types" + + "github.com/cosmos/cosmos-sdk/types/module" + simtypes "github.com/cosmos/cosmos-sdk/types/simulation" + + postssim "github.com/desmos-labs/desmos/v3/x/posts/simulation" + poststypes "github.com/desmos-labs/desmos/v3/x/posts/types" + "github.com/desmos-labs/desmos/v3/x/reports/types" + subspacessim "github.com/desmos-labs/desmos/v3/x/subspaces/simulation" + subspacestypes "github.com/desmos-labs/desmos/v3/x/subspaces/types" +) + +// RandomizeGenState generates a random GenesisState for subspaces +func RandomizeGenState(simState *module.SimulationState) { + // Read the subspaces data + subspacesGenesisBz := simState.GenState[subspacestypes.ModuleName] + var subspacesGenesis subspacestypes.GenesisState + simState.Cdc.MustUnmarshalJSON(subspacesGenesisBz, &subspacesGenesis) + + // Read the posts data + postsGenesisBz := simState.GenState[poststypes.ModuleName] + var postsGenesis poststypes.GenesisState + simState.Cdc.MustUnmarshalJSON(postsGenesisBz, &postsGenesis) + + // Read the relationships data + relationshipsGenesisBz := simState.GenState[relationshipstypes.ModuleName] + var relationshipsGenesis relationshipstypes.GenesisState + simState.Cdc.MustUnmarshalJSON(relationshipsGenesisBz, &relationshipsGenesis) + + reasons := randomReasons(simState.Rand, subspacesGenesis.Subspaces) + reports := randomReports(simState.Rand, simState.Accounts, subspacesGenesis.Subspaces, relationshipsGenesis.Blocks, postsGenesis.GenesisPosts, reasons) + subspacesDataEntries := getSubspacesData(subspacesGenesis.Subspaces, reasons, reports) + params := types.NewParams(GetRandomStandardReasons(simState.Rand, 10)) + + // Create the genesis and sanitize it + reportsGenesis := types.NewGenesisState(subspacesDataEntries, reasons, reports, params) + simState.GenState[types.ModuleName] = simState.Cdc.MustMarshalJSON(reportsGenesis) +} + +// randomReasons return a randomly generated slice of reasons +func randomReasons(r *rand.Rand, subspaces []subspacestypes.Subspace) []types.Reason { + reasonNumber := r.Intn(20) + reasons := make([]types.Reason, reasonNumber) + for i := 0; i < reasonNumber; i++ { + // Get a random subspace + subspace := subspacessim.RandomSubspace(r, subspaces) + + // Generate a random reason + reasons[i] = types.NewReason( + subspace.ID, + uint32(i+1), + GetRandomReasonTitle(r), + GetRandomReasonDescription(r), + ) + } + return reasons +} + +// randomReports returns a randomly generated slice of reports +func randomReports(r *rand.Rand, accs []simtypes.Account, subspaces []subspacestypes.Subspace, blocks []relationshipstypes.UserBlock, genPosts []poststypes.GenesisPost, reasons []types.Reason) []types.Report { + if len(subspaces) == 0 || len(reasons) == 0 { + // No subspaces or valid reasons, so no way we can have a valid post + return nil + } + + reportsNumber := r.Intn(100) + var reports []types.Report + for i := 0; i < reportsNumber; i++ { + // Get a random subspace + subspace := subspacessim.RandomSubspace(r, subspaces) + + // Get a random reporter + reporter, _ := simtypes.RandomAcc(r, accs) + + // Get a random reason + subspaceReasons := getSubspaceReasons(subspace.ID, reasons) + if len(subspaceReasons) == 0 { + continue + } + + reason := RandomReason(r, subspaceReasons) + + var data types.ReportTarget + if r.Intn(101) < 50 { + // 50% of a post report + posts := getSubspacePosts(subspace.ID, genPosts) + if len(posts) == 0 { + continue + } + post := postssim.RandomGenesisPost(r, posts) + if isUserBlocked(reporter.Address.String(), post.Author, subspace.ID, blocks) { + continue + } + data = types.NewPostTarget(post.ID) + } else { + // 50% of a user report + account, _ := simtypes.RandomAcc(r, accs) + if isUserBlocked(reporter.Address.String(), account.Address.String(), subspace.ID, blocks) { + continue + } + data = types.NewUserTarget(account.Address.String()) + } + + // Generate a random report + reports = append(reports, types.NewReport( + subspace.ID, + uint64(i+1), + []uint32{reason.ID}, + GetRandomMessage(r), + data, + reporter.Address.String(), + time.Now(), + )) + } + + return reports +} + +// getSubspaceReasons returns the reporting reasons for the given subspace filtering the given reasons slice +func getSubspaceReasons(subspaceID uint64, reasons []types.Reason) []types.Reason { + var subspaceReasons []types.Reason + for _, reason := range reasons { + if reason.SubspaceID == subspaceID { + subspaceReasons = append(subspaceReasons, reason) + } + } + return subspaceReasons +} + +// isUserBlocked checks if the given user is blocked by the blocker on the provided subspace checking within the blocks +func isUserBlocked(user string, blocker string, subspaceID uint64, blocks []relationshipstypes.UserBlock) bool { + for _, block := range blocks { + if block.Blocked == user && block.Blocker == blocker && block.SubspaceID == subspaceID { + return true + } + } + return false +} + +// getSubspacePosts gets all the posts for the given subspace from the provided slice +func getSubspacePosts(subspaceID uint64, genPosts []poststypes.GenesisPost) []poststypes.GenesisPost { + var posts []poststypes.GenesisPost + for _, post := range genPosts { + if post.SubspaceID == subspaceID { + posts = append(posts, post) + } + } + return posts +} + +// getSubspacesData gets the subspaces data for the provided subspaces +func getSubspacesData(subspaces []subspacestypes.Subspace, reasons []types.Reason, reports []types.Report) []types.SubspaceDataEntry { + entries := make([]types.SubspaceDataEntry, len(subspaces)) + for i, subspace := range subspaces { + // Get the max reason id + maxReasonID := uint32(0) + for _, reason := range reasons { + if reason.SubspaceID == subspace.ID && reason.ID > maxReasonID { + maxReasonID = reason.ID + } + } + + // Get the max report id + maxReportID := uint64(0) + for _, report := range reports { + if report.SubspaceID == subspace.ID && report.ID > maxReportID { + maxReportID = report.ID + } + } + + // Generate the entry + entries[i] = types.NewSubspacesDataEntry( + subspace.ID, + maxReasonID+1, + maxReportID+1, + ) + } + return entries +} diff --git a/x/reports/simulation/operations.go b/x/reports/simulation/operations.go new file mode 100644 index 0000000000..4047c01102 --- /dev/null +++ b/x/reports/simulation/operations.go @@ -0,0 +1,99 @@ +package simulation + +// DONTCOVER + +import ( + "math/rand" + + postskeeper "github.com/desmos-labs/desmos/v3/x/posts/keeper" + subspaceskeeper "github.com/desmos-labs/desmos/v3/x/subspaces/keeper" + + feeskeeper "github.com/desmos-labs/desmos/v3/x/fees/keeper" + + "github.com/cosmos/cosmos-sdk/codec" + simtypes "github.com/cosmos/cosmos-sdk/types/simulation" + authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper" + bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper" + sim "github.com/cosmos/cosmos-sdk/x/simulation" + + "github.com/desmos-labs/desmos/v3/app/params" + "github.com/desmos-labs/desmos/v3/x/reports/keeper" +) + +// Simulation operation weights constants +// #nosec G101 -- This is a false positive +const ( + OpWeightMsgCreateReport = "op_weight_msg_create_report" + OpWeightMsgDeleteReport = "op_weight_msg_delete_report" + OpWeightMsgSupportStandardReason = "op_weight_msg_support_standard_reason" + OpWeightMsgAddReason = "op_weight_msg_add_reason" + OpWeightMsgRemoveReason = "op_weight_msg_remove_reason" + + DefaultGasValue = 200_000 +) + +// WeightedOperations returns all the operations from the module with their respective weights +func WeightedOperations( + appParams simtypes.AppParams, cdc codec.JSONCodec, + k keeper.Keeper, sk subspaceskeeper.Keeper, pk postskeeper.Keeper, + ak authkeeper.AccountKeeper, bk bankkeeper.Keeper, fk feeskeeper.Keeper, +) sim.WeightedOperations { + + var weightMsgCreateReport int + appParams.GetOrGenerate(cdc, OpWeightMsgCreateReport, &weightMsgCreateReport, nil, + func(_ *rand.Rand) { + weightMsgCreateReport = params.DefaultWeightMsgCreateReport + }, + ) + + var weightMsgDeleteReport int + appParams.GetOrGenerate(cdc, OpWeightMsgDeleteReport, &weightMsgDeleteReport, nil, + func(_ *rand.Rand) { + weightMsgDeleteReport = params.DefaultWeightMsgDeleteReport + }, + ) + + var weightMsgSupportStandardReason int + appParams.GetOrGenerate(cdc, OpWeightMsgSupportStandardReason, &weightMsgSupportStandardReason, nil, + func(_ *rand.Rand) { + weightMsgSupportStandardReason = params.DefaultWeightMsgSupportStandardReason + }, + ) + + var weightMsgAddReason int + appParams.GetOrGenerate(cdc, OpWeightMsgAddReason, &weightMsgAddReason, nil, + func(_ *rand.Rand) { + weightMsgAddReason = params.DefaultWeightMsgAddReason + }, + ) + + var weightMsgRemoveReason int + appParams.GetOrGenerate(cdc, OpWeightMsgRemoveReason, &weightMsgRemoveReason, nil, + func(_ *rand.Rand) { + weightMsgRemoveReason = params.DefaultWeightMsgRemoveReason + }, + ) + + return sim.WeightedOperations{ + sim.NewWeightedOperation( + weightMsgCreateReport, + SimulateMsgCreateReport(k, sk, pk, ak, bk, fk), + ), + sim.NewWeightedOperation( + weightMsgDeleteReport, + SimulateMsgDeleteReport(k, sk, ak, bk, fk), + ), + sim.NewWeightedOperation( + weightMsgSupportStandardReason, + SimulateMsgSupportStandardReason(k, sk, ak, bk, fk), + ), + sim.NewWeightedOperation( + weightMsgAddReason, + SimulateMsgAddReason(sk, ak, bk, fk), + ), + sim.NewWeightedOperation( + weightMsgRemoveReason, + SimulateMsgRemoveReason(k, sk, ak, bk, fk), + ), + } +} diff --git a/x/reports/simulation/operations_reasons.go b/x/reports/simulation/operations_reasons.go new file mode 100644 index 0000000000..478854d9c0 --- /dev/null +++ b/x/reports/simulation/operations_reasons.go @@ -0,0 +1,247 @@ +package simulation + +// DONTCOVER + +import ( + "math/rand" + + feeskeeper "github.com/desmos-labs/desmos/v3/x/fees/keeper" + subspaceskeeper "github.com/desmos-labs/desmos/v3/x/subspaces/keeper" + subspacessim "github.com/desmos-labs/desmos/v3/x/subspaces/simulation" + subspacestypes "github.com/desmos-labs/desmos/v3/x/subspaces/types" + + "github.com/desmos-labs/desmos/v3/testutil/simtesting" + + "github.com/cosmos/cosmos-sdk/baseapp" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + sdk "github.com/cosmos/cosmos-sdk/types" + simtypes "github.com/cosmos/cosmos-sdk/types/simulation" + authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper" + bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper" + + "github.com/desmos-labs/desmos/v3/x/reports/keeper" + "github.com/desmos-labs/desmos/v3/x/reports/types" +) + +// SimulateMsgSupportStandardReason tests and runs a single MsgSupportStandardReason +func SimulateMsgSupportStandardReason( + k keeper.Keeper, sk subspaceskeeper.Keeper, + ak authkeeper.AccountKeeper, bk bankkeeper.Keeper, fk feeskeeper.Keeper, +) simtypes.Operation { + return func( + r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, + accs []simtypes.Account, chainID string, + ) (simtypes.OperationMsg, []simtypes.FutureOperation, error) { + + // Get the data + subspaceID, standardReasonID, signer, skip := randomSupportStandardReasonFields(r, ctx, accs, sk, k) + if skip { + return simtypes.NoOpMsg(types.RouterKey, types.ModuleName, "MsgSupportStandardReason"), nil, nil + } + + // Build the message + msg := types.NewMsgSupportStandardReason(subspaceID, standardReasonID, signer.Address.String()) + + // Send the message + err := simtesting.SendMsg(r, app, ak, bk, fk, msg, ctx, chainID, DefaultGasValue, []cryptotypes.PrivKey{signer.PrivKey}) + if err != nil { + return simtypes.NoOpMsg(types.RouterKey, types.ModuleName, "MsgSupportStandardReason"), nil, err + } + + return simtypes.NewOperationMsg(msg, true, "MsgSupportStandardReason", nil), nil, nil + } +} + +// randomSupportStandardReasonFields returns the data used to build a random MsgSupportStandardReason +func randomSupportStandardReasonFields( + r *rand.Rand, ctx sdk.Context, accs []simtypes.Account, sk subspaceskeeper.Keeper, k keeper.Keeper, +) (subspaceID uint64, standardReasonID uint32, user simtypes.Account, skip bool) { + // Get the user + if len(accs) == 0 { + // Skip because there are no accounts + skip = true + return + } + user, _ = simtypes.RandomAcc(r, accs) + + // Get a subspace id + subspaces := sk.GetAllSubspaces(ctx) + if len(subspaces) == 0 { + // Skip because there are no subspaces + skip = true + return + } + subspace := subspacessim.RandomSubspace(r, subspaces) + subspaceID = subspace.ID + + // Get a reason + reasons := k.GetParams(ctx).StandardReasons + if len(reasons) == 0 { + // Skip because there are no standard reasons to support + skip = true + return + } + reason := RandomStandardReason(r, reasons) + standardReasonID = reason.ID + + // Get a user + users, _ := sk.GetUsersWithRootPermission(ctx, subspace.ID, subspacestypes.PermissionManageReasons) + acc := subspacessim.GetAccount(subspacessim.RandomAddress(r, users), accs) + if acc == nil { + // Skip the operation without error as the account is not valid + skip = true + return + } + user = *acc + + return subspaceID, standardReasonID, user, false +} + +// -------------------------------------------------------------------------------------------------------------------- + +// SimulateMsgAddReason tests and runs a single MsgAddReason +func SimulateMsgAddReason( + sk subspaceskeeper.Keeper, ak authkeeper.AccountKeeper, bk bankkeeper.Keeper, fk feeskeeper.Keeper, +) simtypes.Operation { + return func( + r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, + accs []simtypes.Account, chainID string, + ) (simtypes.OperationMsg, []simtypes.FutureOperation, error) { + + // Get the data + data, signer, skip := randomAddReasonFields(r, ctx, accs, sk) + if skip { + return simtypes.NoOpMsg(types.RouterKey, types.ModuleName, "MsgAddReason"), nil, nil + } + + // Build the message + msg := types.NewMsgAddReason(data.SubspaceID, data.Title, data.Description, signer.Address.String()) + + // Send the message + err := simtesting.SendMsg(r, app, ak, bk, fk, msg, ctx, chainID, DefaultGasValue, []cryptotypes.PrivKey{signer.PrivKey}) + if err != nil { + return simtypes.NoOpMsg(types.RouterKey, types.ModuleName, "MsgAddReason"), nil, err + } + + return simtypes.NewOperationMsg(msg, true, "MsgAddReason", nil), nil, nil + } +} + +// randomAddReasonFields returns the data used to build a random MsgAddReason +func randomAddReasonFields( + r *rand.Rand, ctx sdk.Context, accs []simtypes.Account, sk subspaceskeeper.Keeper, +) (data types.Reason, user simtypes.Account, skip bool) { + // Get the user + if len(accs) == 0 { + // Skip because there are no accounts + skip = true + return + } + user, _ = simtypes.RandomAcc(r, accs) + + // Get a subspace id + subspaces := sk.GetAllSubspaces(ctx) + if len(subspaces) == 0 { + // Skip because there are no subspaces + skip = true + return + } + subspace := subspacessim.RandomSubspace(r, subspaces) + subspaceID := subspace.ID + + // Generate a random reason + reason := types.NewReason( + subspaceID, + 0, + GetRandomReasonTitle(r), + GetRandomReasonDescription(r), + ) + + // Get a user + users, _ := sk.GetUsersWithRootPermission(ctx, subspace.ID, subspacestypes.PermissionManageReasons) + acc := subspacessim.GetAccount(subspacessim.RandomAddress(r, users), accs) + if acc == nil { + // Skip the operation without error as the account is not valid + skip = true + return + } + user = *acc + + return reason, user, false +} + +// -------------------------------------------------------------------------------------------------------------------- + +// SimulateMsgRemoveReason tests and runs a single MsgRemoveReason +func SimulateMsgRemoveReason( + k keeper.Keeper, sk subspaceskeeper.Keeper, + ak authkeeper.AccountKeeper, bk bankkeeper.Keeper, fk feeskeeper.Keeper, +) simtypes.Operation { + return func( + r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, + accs []simtypes.Account, chainID string, + ) (simtypes.OperationMsg, []simtypes.FutureOperation, error) { + + // Get the data + subspaceID, reasonID, signer, skip := randomRemoveReason(r, ctx, accs, k, sk) + if skip { + return simtypes.NoOpMsg(types.RouterKey, types.ModuleName, "MsgRemoveReason"), nil, nil + } + + // Build the message + msg := types.NewMsgRemoveReason(subspaceID, reasonID, signer.Address.String()) + + // Send the message + err := simtesting.SendMsg(r, app, ak, bk, fk, msg, ctx, chainID, DefaultGasValue, []cryptotypes.PrivKey{signer.PrivKey}) + if err != nil { + return simtypes.NoOpMsg(types.RouterKey, types.ModuleName, "MsgRemoveReason"), nil, err + } + + return simtypes.NewOperationMsg(msg, true, "MsgRemoveReason", nil), nil, nil + } +} + +// randomRemoveReason returns the data used to build a random MsgRemoveReason +func randomRemoveReason( + r *rand.Rand, ctx sdk.Context, accs []simtypes.Account, k keeper.Keeper, sk subspaceskeeper.Keeper, +) (subspaceID uint64, reasonID uint32, user simtypes.Account, skip bool) { + // Get the user + if len(accs) == 0 { + // Skip because there are no accounts + skip = true + return + } + user, _ = simtypes.RandomAcc(r, accs) + + // Get a subspace id + subspaces := sk.GetAllSubspaces(ctx) + if len(subspaces) == 0 { + // Skip because there are no subspaces + skip = true + return + } + subspace := subspacessim.RandomSubspace(r, subspaces) + subspaceID = subspace.ID + + // Get a random reason + reasons := k.GetSubspaceReasons(ctx, subspaceID) + if len(reasons) == 0 { + // Skip because there are no reasons to delete + skip = true + return + } + reason := RandomReason(r, reasons) + reasonID = reason.ID + + // Get a user + users, _ := sk.GetUsersWithRootPermission(ctx, subspace.ID, subspacestypes.PermissionManageReasons) + acc := subspacessim.GetAccount(subspacessim.RandomAddress(r, users), accs) + if acc == nil { + // Skip the operation without error as the account is not valid + skip = true + return + } + user = *acc + + return subspaceID, reasonID, user, false +} diff --git a/x/reports/simulation/operations_reports.go b/x/reports/simulation/operations_reports.go new file mode 100644 index 0000000000..464d9a0fd3 --- /dev/null +++ b/x/reports/simulation/operations_reports.go @@ -0,0 +1,203 @@ +package simulation + +// DONTCOVER + +import ( + "math/rand" + "time" + + postskeeper "github.com/desmos-labs/desmos/v3/x/posts/keeper" + postssim "github.com/desmos-labs/desmos/v3/x/posts/simulation" + subspaceskeeper "github.com/desmos-labs/desmos/v3/x/subspaces/keeper" + subspacessim "github.com/desmos-labs/desmos/v3/x/subspaces/simulation" + subspacestypes "github.com/desmos-labs/desmos/v3/x/subspaces/types" + + feeskeeper "github.com/desmos-labs/desmos/v3/x/fees/keeper" + + "github.com/desmos-labs/desmos/v3/testutil/simtesting" + + "github.com/cosmos/cosmos-sdk/baseapp" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + sdk "github.com/cosmos/cosmos-sdk/types" + simtypes "github.com/cosmos/cosmos-sdk/types/simulation" + authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper" + bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper" + + "github.com/desmos-labs/desmos/v3/x/reports/keeper" + "github.com/desmos-labs/desmos/v3/x/reports/types" +) + +// SimulateMsgCreateReport tests and runs a single MsgCreateReport +func SimulateMsgCreateReport( + k keeper.Keeper, sk subspaceskeeper.Keeper, pk postskeeper.Keeper, + ak authkeeper.AccountKeeper, bk bankkeeper.Keeper, fk feeskeeper.Keeper, +) simtypes.Operation { + return func( + r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, + accs []simtypes.Account, chainID string, + ) (simtypes.OperationMsg, []simtypes.FutureOperation, error) { + + // Get the data + data, creator, skip := randomCreateReportFields(r, ctx, accs, sk, pk, k) + if skip { + return simtypes.NoOpMsg(types.RouterKey, types.ModuleName, "MsgCreateReport"), nil, nil + } + + // Build the message + msg := types.NewMsgCreateReport( + data.SubspaceID, + data.ReasonsIDs, + data.Message, + data.Target.GetCachedValue().(types.ReportTarget), + creator.Address.String(), + ) + + // Send the message + err := simtesting.SendMsg(r, app, ak, bk, fk, msg, ctx, chainID, DefaultGasValue, []cryptotypes.PrivKey{creator.PrivKey}) + if err != nil { + return simtypes.NoOpMsg(types.RouterKey, types.ModuleName, "MsgCreateReport"), nil, err + } + + return simtypes.NewOperationMsg(msg, true, "MsgCreateReport", nil), nil, nil + } +} + +// randomCreateReportFields returns the data used to build a random MsgCreateReport +func randomCreateReportFields( + r *rand.Rand, ctx sdk.Context, accs []simtypes.Account, sk subspaceskeeper.Keeper, pk postskeeper.Keeper, k keeper.Keeper, +) (report types.Report, creator simtypes.Account, skip bool) { + // Get the creator + if len(accs) == 0 { + // Skip because there are no accounts + skip = true + return + } + creator, _ = simtypes.RandomAcc(r, accs) + + // Get a subspace id + subspaces := sk.GetAllSubspaces(ctx) + if len(subspaces) == 0 { + // Skip because there are no subspaces + skip = true + return + } + subspace := subspacessim.RandomSubspace(r, subspaces) + subspaceID := subspace.ID + + // Get a reason + reasons := k.GetSubspaceReasons(ctx, subspaceID) + if len(reasons) == 0 { + // Skip because there are no reasons to report the data for + skip = true + return + } + reason := RandomReason(r, reasons) + + // Get a report target + var data types.ReportTarget + if r.Intn(101) < 50 { + // 50% of having a user report + user, _ := simtypes.RandomAcc(r, accs) + data = types.NewUserTarget(user.Address.String()) + } else { + posts := pk.GetSubspacePosts(ctx, subspaceID) + if len(posts) == 0 { + // Skip because there are no posts to be reported + skip = true + return + } + post := postssim.RandomPost(r, posts) + data = types.NewPostTarget(post.ID) + } + + // Get a reporter + reporters, _ := sk.GetUsersWithRootPermission(ctx, subspace.ID, subspacestypes.PermissionReportContent) + acc := subspacessim.GetAccount(subspacessim.RandomAddress(r, reporters), accs) + if acc == nil { + // Skip the operation without error as the account is not valid + skip = true + return + } + creator = *acc + + // Get the report target + report = types.NewReport( + subspaceID, + 0, + []uint32{reason.ID}, + GetRandomMessage(r), + data, + creator.Address.String(), + time.Time{}, + ) + + return report, creator, false +} + +// -------------------------------------------------------------------------------------------------------------------- + +// SimulateMsgDeleteReport tests and runs a single msg delete subspace +func SimulateMsgDeleteReport( + k keeper.Keeper, sk subspaceskeeper.Keeper, + ak authkeeper.AccountKeeper, bk bankkeeper.Keeper, fk feeskeeper.Keeper, +) simtypes.Operation { + return func( + r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, + accs []simtypes.Account, chainID string, + ) (simtypes.OperationMsg, []simtypes.FutureOperation, error) { + + // Get the data + subspaceID, reportID, editor, skip := randomDeleteReportFields(r, ctx, accs, k, sk) + if skip { + return simtypes.NoOpMsg(types.RouterKey, types.ModuleName, "MsgDeleteReport"), nil, nil + } + + // Build the message + msg := types.NewMsgDeleteReport(subspaceID, reportID, editor.Address.String()) + + // Send the data + err := simtesting.SendMsg(r, app, ak, bk, fk, msg, ctx, chainID, 1_500_000, []cryptotypes.PrivKey{editor.PrivKey}) + if err != nil { + return simtypes.NoOpMsg(types.RouterKey, types.ModuleName, "MsgDeleteReport"), nil, err + } + + return simtypes.NewOperationMsg(msg, true, "MsgDeleteReport", nil), nil, nil + } +} + +// randomDeleteReportFields returns the data needed to delete a subspace +func randomDeleteReportFields( + r *rand.Rand, ctx sdk.Context, accs []simtypes.Account, k keeper.Keeper, sk subspaceskeeper.Keeper, +) (subspaceID uint64, reportID uint64, account simtypes.Account, skip bool) { + // Get a subspace id + subspaces := sk.GetAllSubspaces(ctx) + if len(subspaces) == 0 { + // Skip because there are no subspaces + skip = true + return + } + subspace := subspacessim.RandomSubspace(r, subspaces) + subspaceID = subspace.ID + + // Get a report + reports := k.GetSubspaceReports(ctx, subspaceID) + if len(reports) == 0 { + // Skip because there are no reports + skip = true + return + } + report := RandomReport(r, reports) + reportID = report.ID + + // Get an editor + editors, _ := sk.GetUsersWithRootPermission(ctx, subspace.ID, subspacestypes.PermissionManageReports) + acc := subspacessim.GetAccount(subspacessim.RandomAddress(r, editors), accs) + if acc == nil { + // Skip the operation without error as the account is not valid + skip = true + return + } + account = *acc + + return subspaceID, reportID, account, false +} diff --git a/x/reports/simulation/params.go b/x/reports/simulation/params.go new file mode 100644 index 0000000000..475d51c860 --- /dev/null +++ b/x/reports/simulation/params.go @@ -0,0 +1,29 @@ +package simulation + +// DONTCOVER + +import ( + "encoding/json" + "math/rand" + + "github.com/cosmos/cosmos-sdk/x/simulation" + + simtypes "github.com/cosmos/cosmos-sdk/types/simulation" + + "github.com/desmos-labs/desmos/v3/x/reports/types" +) + +func ParamChanges(_ *rand.Rand) []simtypes.ParamChange { + return []simtypes.ParamChange{ + simulation.NewSimParamChange(types.ModuleName, string(types.ReasonsKey), + func(r *rand.Rand) string { + standardReasons := GetRandomStandardReasons(r, 10) + bz, err := json.Marshal(&standardReasons) + if err != nil { + panic(err) + } + return string(bz) + }, + ), + } +} diff --git a/x/reports/simulation/utils.go b/x/reports/simulation/utils.go new file mode 100644 index 0000000000..8aaed14805 --- /dev/null +++ b/x/reports/simulation/utils.go @@ -0,0 +1,54 @@ +package simulation + +// DONTCOVER + +import ( + "math/rand" + + simtypes "github.com/cosmos/cosmos-sdk/types/simulation" + + "github.com/desmos-labs/desmos/v3/x/reports/types" +) + +// RandomStandardReason returns a random standard reason from the given slice +func RandomStandardReason(r *rand.Rand, reasons []types.StandardReason) types.StandardReason { + return reasons[r.Intn(len(reasons))] +} + +// RandomReason returns a random reason from the given slice +func RandomReason(r *rand.Rand, reasons []types.Reason) types.Reason { + return reasons[r.Intn(len(reasons))] +} + +// GetRandomReasonTitle returns a random reason title +func GetRandomReasonTitle(r *rand.Rand) string { + return simtypes.RandStringOfLength(r, 30) +} + +// GetRandomReasonDescription returns a random reason description +func GetRandomReasonDescription(r *rand.Rand) string { + return simtypes.RandStringOfLength(r, 50) +} + +// GetRandomMessage returns a random reporting message +func GetRandomMessage(r *rand.Rand) string { + return simtypes.RandStringOfLength(r, 30) +} + +// RandomReport returns a random report from the given slice +func RandomReport(r *rand.Rand, reports []types.Report) types.Report { + return reports[r.Intn(len(reports))] +} + +// GetRandomStandardReasons returns a randomly generated slice of standard reason +func GetRandomStandardReasons(r *rand.Rand, num int) []types.StandardReason { + standardReasons := make([]types.StandardReason, num) + for i := 0; i < num; i++ { + standardReasons[i] = types.NewStandardReason( + uint32(i+1), + GetRandomReasonTitle(r), + GetRandomReasonDescription(r), + ) + } + return standardReasons +} diff --git a/x/reports/types/codec.go b/x/reports/types/codec.go new file mode 100644 index 0000000000..bafca8f17d --- /dev/null +++ b/x/reports/types/codec.go @@ -0,0 +1,59 @@ +package types + +import ( + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/codec/types" + cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/msgservice" +) + +func RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) { + cdc.RegisterInterface((*ReportTarget)(nil), nil) + cdc.RegisterConcrete(&UserTarget{}, "desmos/UserTarget", nil) + cdc.RegisterConcrete(&PostTarget{}, "desmos/PostTarget", nil) + + cdc.RegisterConcrete(MsgCreateReport{}, "desmos/MsgCreateReport", nil) + cdc.RegisterConcrete(MsgDeleteReport{}, "desmos/MsgDeleteReport", nil) + cdc.RegisterConcrete(MsgSupportStandardReason{}, "desmos/MsgSupportStandardReason", nil) + cdc.RegisterConcrete(MsgAddReason{}, "desmos/MsgAddReason", nil) + cdc.RegisterConcrete(MsgRemoveReason{}, "desmos/MsgRemoveReason", nil) +} + +func RegisterInterfaces(registry types.InterfaceRegistry) { + registry.RegisterInterface( + "desmos.reports.v1.ReportTarget", + (*ReportTarget)(nil), + &UserTarget{}, + &PostTarget{}, + ) + + registry.RegisterImplementations((*sdk.Msg)(nil), + &MsgCreateReport{}, + &MsgDeleteReport{}, + &MsgSupportStandardReason{}, + &MsgAddReason{}, + &MsgRemoveReason{}, + ) + + msgservice.RegisterMsgServiceDesc(registry, &_Msg_serviceDesc) +} + +var ( + amino = codec.NewLegacyAmino() + + // AminoCdc references the global x/reports module codec. Note, the codec should + // ONLY be used in certain instances of tests and for JSON encoding as Amino is + // still used for that purpose. + // + // The actual codec used for serialization should be provided to x/reports and + // defined at the application level. + AminoCdc = codec.NewAminoCodec(amino) + + ModuleCdc = codec.NewProtoCodec(types.NewInterfaceRegistry()) +) + +func init() { + RegisterLegacyAminoCodec(amino) + cryptocodec.RegisterCrypto(amino) +} diff --git a/x/reports/types/errors.go b/x/reports/types/errors.go new file mode 100644 index 0000000000..1908087250 --- /dev/null +++ b/x/reports/types/errors.go @@ -0,0 +1,11 @@ +package types + +// DONTCOVER + +import ( + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" +) + +var ( + ErrInvalidGenesis = sdkerrors.Register(ModuleName, 1, "invalid genesis state") +) diff --git a/x/reports/types/events.go b/x/reports/types/events.go new file mode 100644 index 0000000000..52c6918209 --- /dev/null +++ b/x/reports/types/events.go @@ -0,0 +1,23 @@ +package types + +// DONTCOVER + +const ( + EventTypeCreateReport = "create_report" + EventTypeReportPost = "report_post" + EventTypeReportUser = "report_user" + EventTypeDeleteReport = "delete_report" + EventTypeSupportStandardReason = "support_standard_reason" + EventTypeAddReason = "add_reason" + EventTypeRemoveReason = "remove_reason" + + AttributeValueCategory = ModuleName + AttributeKeySubspaceID = "subspace_id" + AttributeKeyReportID = "report_id" + AttributeKeyReporter = "reporter" + AttributeKeyCreationTime = "creation_time" + AttributeKeyStandardReasonID = "standard_reason_id" + AttributeKeyReasonID = "reason_id" + AttributeKeyPostID = "post_id" + AttributeKeyUser = "user" +) diff --git a/x/reports/types/expected_keepers.go b/x/reports/types/expected_keepers.go new file mode 100644 index 0000000000..c1c847c68d --- /dev/null +++ b/x/reports/types/expected_keepers.go @@ -0,0 +1,36 @@ +package types + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + + poststypes "github.com/desmos-labs/desmos/v3/x/posts/types" + + subspacestypes "github.com/desmos-labs/desmos/v3/x/subspaces/types" +) + +// SubspacesKeeper represents a keeper that deals with subspaces +type SubspacesKeeper interface { + // HasSubspace tells whether the subspace with the given id exists or not + HasSubspace(ctx sdk.Context, subspaceID uint64) bool + + // HasPermission tells whether the given user has the provided permission inside the subspace with the specified id + HasPermission(ctx sdk.Context, subspaceID uint64, sectionID uint32, user string, permission subspacestypes.Permission) bool + + // IterateSubspaces iterates through the subspaces set and performs the given function + IterateSubspaces(ctx sdk.Context, fn func(subspace subspacestypes.Subspace) (stop bool)) +} + +// RelationshipsKeeper represents a keeper that deals with relationships +type RelationshipsKeeper interface { + // HasUserBlocked tells whether the given blocker has blocked the user inside the provided subspace + HasUserBlocked(ctx sdk.Context, blocker, user string, subspaceID uint64) bool +} + +// PostsKeeper represents a keeper that deals with posts +type PostsKeeper interface { + // HasPost tells whether the given post exists or not + HasPost(ctx sdk.Context, subspaceID uint64, postID uint64) bool + + // GetPost returns the post associated with the given id. + GetPost(ctx sdk.Context, subspaceID uint64, postID uint64) (poststypes.Post, bool) +} diff --git a/x/reports/types/genesis.go b/x/reports/types/genesis.go new file mode 100644 index 0000000000..11c841db5d --- /dev/null +++ b/x/reports/types/genesis.go @@ -0,0 +1,135 @@ +package types + +import ( + "fmt" + + "github.com/cosmos/cosmos-sdk/codec/types" +) + +// NewGenesisState returns a new genesis state instance +func NewGenesisState(subspaces []SubspaceDataEntry, reasons []Reason, reports []Report, params Params) *GenesisState { + return &GenesisState{ + SubspacesData: subspaces, + Reasons: reasons, + Reports: reports, + Params: params, + } +} + +// UnpackInterfaces implements UnpackInterfacesMessage.UnpackInterfaces +func (data GenesisState) UnpackInterfaces(unpacker types.AnyUnpacker) error { + for _, report := range data.Reports { + err := report.UnpackInterfaces(unpacker) + if err != nil { + return err + } + } + return nil +} + +// DefaultGenesisState returns a DefaultGenesisState +func DefaultGenesisState() *GenesisState { + return NewGenesisState(nil, nil, nil, DefaultParams()) +} + +// ValidateGenesis validates the given genesis state and returns an error if something is invalid +func ValidateGenesis(data *GenesisState) error { + for _, subspaceData := range data.SubspacesData { + if containsDuplicatedSubspacesData(data.SubspacesData, subspaceData) { + return fmt.Errorf("duplicated subspace entry for subspace id %d", subspaceData.SubspaceID) + } + + err := subspaceData.Validate() + if err != nil { + return fmt.Errorf("invalid subspace data: %s", err) + } + } + + for _, reason := range data.Reasons { + if containsDuplicatedReason(data.Reasons, reason) { + return fmt.Errorf("duplicate reason: subspace id %d, reason id %d", reason.SubspaceID, reason.ID) + } + + err := reason.Validate() + if err != nil { + return fmt.Errorf("invalid reason: %s", err) + } + } + + for _, report := range data.Reports { + if containsDuplicatedReport(data.Reports, report) { + return fmt.Errorf("duplicated report: subspace id %d, report id %d", report.SubspaceID, report.ID) + } + + err := report.Validate() + if err != nil { + return fmt.Errorf("invalid report: %s", err) + } + } + + return data.Params.Validate() +} + +// containsDuplicatedSubspacesData tells whether the given subspaces data slice +// contains a duplicated entry for the same subspace id as the one given +func containsDuplicatedSubspacesData(subspaces []SubspaceDataEntry, data SubspaceDataEntry) bool { + var count = 0 + for _, entry := range subspaces { + if entry.SubspaceID == data.SubspaceID { + count++ + } + } + return count > 1 +} + +// containsDuplicatedReason tells whether the given reasons slice contains +// a duplicated reason based on the same subspace id and reason id of the given one +func containsDuplicatedReason(reasons []Reason, reason Reason) bool { + var count = 0 + for _, r := range reasons { + if r.SubspaceID == reason.SubspaceID && r.ID == reason.ID { + count++ + } + } + return count > 1 +} + +// containsDuplicatedReport tells whether the given reports slice contains +// a duplicated report based on the same subspace id and report id of the given one +func containsDuplicatedReport(reports []Report, report Report) bool { + var count = 0 + for _, r := range reports { + if r.SubspaceID == report.SubspaceID && r.ID == report.ID { + count++ + } + } + return count > 1 +} + +// -------------------------------------------------------------------------------------------------------------------- + +// NewSubspacesDataEntry returns a new SubspaceDataEntry instance +func NewSubspacesDataEntry(subspaceID uint64, reasonID uint32, reportID uint64) SubspaceDataEntry { + return SubspaceDataEntry{ + SubspaceID: subspaceID, + ReportID: reportID, + ReasonID: reasonID, + } +} + +// Validate implements fmt.Validator +func (data SubspaceDataEntry) Validate() error { + if data.SubspaceID == 0 { + return fmt.Errorf("invalid subspace id: %d", data.SubspaceID) + } + + if data.ReasonID == 0 { + return fmt.Errorf("invalid reason id: %d", data.ReasonID) + } + + if data.ReportID == 0 { + return fmt.Errorf("invalid report id: %d", data.ReportID) + } + + return nil +} diff --git a/x/reports/types/genesis.pb.go b/x/reports/types/genesis.pb.go new file mode 100644 index 0000000000..a5f364810e --- /dev/null +++ b/x/reports/types/genesis.pb.go @@ -0,0 +1,723 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: desmos/reports/v1/genesis.proto + +package types + +import ( + fmt "fmt" + _ "github.com/cosmos/cosmos-sdk/codec/types" + _ "github.com/gogo/protobuf/gogoproto" + proto "github.com/gogo/protobuf/proto" + _ "github.com/regen-network/cosmos-proto" + _ "google.golang.org/protobuf/types/known/timestamppb" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +// GenesisState defines the reports module's genesis state. +type GenesisState struct { + SubspacesData []SubspaceDataEntry `protobuf:"bytes,1,rep,name=subspaces_data,json=subspacesData,proto3" json:"subspaces_data"` + Reasons []Reason `protobuf:"bytes,2,rep,name=reasons,proto3" json:"reasons"` + Reports []Report `protobuf:"bytes,3,rep,name=reports,proto3" json:"reports"` + Params Params `protobuf:"bytes,4,opt,name=params,proto3" json:"params"` +} + +func (m *GenesisState) Reset() { *m = GenesisState{} } +func (m *GenesisState) String() string { return proto.CompactTextString(m) } +func (*GenesisState) ProtoMessage() {} +func (*GenesisState) Descriptor() ([]byte, []int) { + return fileDescriptor_e4c495c10dc36e45, []int{0} +} +func (m *GenesisState) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *GenesisState) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_GenesisState.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *GenesisState) XXX_Merge(src proto.Message) { + xxx_messageInfo_GenesisState.Merge(m, src) +} +func (m *GenesisState) XXX_Size() int { + return m.Size() +} +func (m *GenesisState) XXX_DiscardUnknown() { + xxx_messageInfo_GenesisState.DiscardUnknown(m) +} + +var xxx_messageInfo_GenesisState proto.InternalMessageInfo + +// SubspaceDataEntry contains the data related to a single subspace +type SubspaceDataEntry struct { + // Id of the subspace to which the data relates + SubspaceID uint64 `protobuf:"varint,1,opt,name=subspace_id,json=subspaceId,proto3" json:"subspace_id,omitempty"` + // Id of the next reason inside the subspace + ReasonID uint32 `protobuf:"varint,2,opt,name=reason_id,json=reasonId,proto3" json:"reason_id,omitempty"` + // Id of the next report inside the subspace + ReportID uint64 `protobuf:"varint,3,opt,name=report_id,json=reportId,proto3" json:"report_id,omitempty"` +} + +func (m *SubspaceDataEntry) Reset() { *m = SubspaceDataEntry{} } +func (m *SubspaceDataEntry) String() string { return proto.CompactTextString(m) } +func (*SubspaceDataEntry) ProtoMessage() {} +func (*SubspaceDataEntry) Descriptor() ([]byte, []int) { + return fileDescriptor_e4c495c10dc36e45, []int{1} +} +func (m *SubspaceDataEntry) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *SubspaceDataEntry) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_SubspaceDataEntry.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *SubspaceDataEntry) XXX_Merge(src proto.Message) { + xxx_messageInfo_SubspaceDataEntry.Merge(m, src) +} +func (m *SubspaceDataEntry) XXX_Size() int { + return m.Size() +} +func (m *SubspaceDataEntry) XXX_DiscardUnknown() { + xxx_messageInfo_SubspaceDataEntry.DiscardUnknown(m) +} + +var xxx_messageInfo_SubspaceDataEntry proto.InternalMessageInfo + +func (m *SubspaceDataEntry) GetSubspaceID() uint64 { + if m != nil { + return m.SubspaceID + } + return 0 +} + +func (m *SubspaceDataEntry) GetReasonID() uint32 { + if m != nil { + return m.ReasonID + } + return 0 +} + +func (m *SubspaceDataEntry) GetReportID() uint64 { + if m != nil { + return m.ReportID + } + return 0 +} + +func init() { + proto.RegisterType((*GenesisState)(nil), "desmos.reports.v1.GenesisState") + proto.RegisterType((*SubspaceDataEntry)(nil), "desmos.reports.v1.SubspaceDataEntry") +} + +func init() { proto.RegisterFile("desmos/reports/v1/genesis.proto", fileDescriptor_e4c495c10dc36e45) } + +var fileDescriptor_e4c495c10dc36e45 = []byte{ + // 408 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x92, 0xbd, 0x8e, 0xda, 0x40, + 0x14, 0x85, 0x3d, 0x60, 0x11, 0x32, 0xfc, 0x48, 0x58, 0x29, 0x0c, 0x85, 0x6d, 0xa1, 0x14, 0xa4, + 0x88, 0x27, 0x84, 0x22, 0x4a, 0x4a, 0x8b, 0x28, 0x22, 0x55, 0x62, 0xba, 0x34, 0x68, 0x8c, 0x27, + 0x8e, 0x25, 0xec, 0xb1, 0x3c, 0x03, 0x0a, 0x6f, 0x90, 0x32, 0x55, 0x8a, 0x54, 0x79, 0x1c, 0x4a, + 0xca, 0x54, 0x68, 0x65, 0x5e, 0x64, 0x35, 0x3f, 0x66, 0xa5, 0x65, 0x77, 0xbb, 0x3b, 0x3e, 0xdf, + 0x39, 0xbe, 0x47, 0xba, 0xd0, 0x8d, 0x09, 0xcb, 0x28, 0x43, 0x25, 0x29, 0x68, 0xc9, 0x19, 0xda, + 0x4d, 0x51, 0x42, 0x72, 0xc2, 0x52, 0xe6, 0x17, 0x25, 0xe5, 0xd4, 0x1a, 0x28, 0xc0, 0xd7, 0x80, + 0xbf, 0x9b, 0x8e, 0x5e, 0x24, 0x34, 0xa1, 0x52, 0x45, 0x62, 0x52, 0xe0, 0x68, 0x98, 0x50, 0x9a, + 0x6c, 0x08, 0x92, 0xaf, 0x68, 0xfb, 0x1d, 0xe1, 0x7c, 0xaf, 0x25, 0xf7, 0xbe, 0xc4, 0xd3, 0x8c, + 0x30, 0x8e, 0xb3, 0xa2, 0xf6, 0xae, 0xa9, 0xf8, 0xc9, 0x4a, 0x85, 0xaa, 0x87, 0x96, 0x9c, 0xeb, + 0x05, 0x33, 0x1a, 0x93, 0x8d, 0xd6, 0xc7, 0x7f, 0x1a, 0xb0, 0xfb, 0x49, 0x6d, 0xbc, 0xe4, 0x98, + 0x13, 0xeb, 0x2b, 0xec, 0xb3, 0x6d, 0xc4, 0x0a, 0xbc, 0x26, 0x6c, 0x15, 0x63, 0x8e, 0x6d, 0xe0, + 0x35, 0x27, 0x9d, 0xb7, 0x2f, 0xfd, 0xab, 0x26, 0xfe, 0x52, 0x83, 0x73, 0xcc, 0xf1, 0xc7, 0x9c, + 0x97, 0xfb, 0xc0, 0x3c, 0x9c, 0x5c, 0x23, 0xec, 0x5d, 0x12, 0x84, 0x62, 0xbd, 0x87, 0xcf, 0x4a, + 0x82, 0x19, 0xcd, 0x99, 0xdd, 0x90, 0x59, 0xc3, 0x07, 0xb2, 0x42, 0x49, 0xe8, 0x80, 0x9a, 0x57, + 0x56, 0xc9, 0xd8, 0xcd, 0x27, 0xac, 0x62, 0xbc, 0xb3, 0x4a, 0xc1, 0x7a, 0x07, 0x5b, 0x05, 0x2e, + 0x71, 0xc6, 0x6c, 0xd3, 0x03, 0x8f, 0x38, 0xbf, 0x48, 0x40, 0x3b, 0x35, 0xfe, 0xc1, 0xfc, 0xf5, + 0xcf, 0x35, 0xc6, 0x7f, 0x01, 0x1c, 0x5c, 0xf5, 0xb3, 0x10, 0xec, 0xd4, 0xdd, 0x56, 0x69, 0x6c, + 0x03, 0x0f, 0x4c, 0xcc, 0xa0, 0x5f, 0x9d, 0x5c, 0x58, 0xb3, 0x8b, 0x79, 0x08, 0x6b, 0x64, 0x11, + 0x5b, 0xaf, 0xe0, 0x73, 0xd5, 0x45, 0xe0, 0x0d, 0x0f, 0x4c, 0x7a, 0x41, 0xb7, 0x3a, 0xb9, 0x6d, + 0x55, 0x77, 0x31, 0x0f, 0xdb, 0x4a, 0xae, 0x51, 0xb1, 0x9a, 0x40, 0x9b, 0x32, 0x59, 0xa3, 0xe2, + 0xa3, 0x42, 0xe5, 0x14, 0x07, 0x9f, 0x0f, 0x95, 0x03, 0x8e, 0x95, 0x03, 0x6e, 0x2a, 0x07, 0xfc, + 0x3e, 0x3b, 0xc6, 0xf1, 0xec, 0x18, 0xff, 0xcf, 0x8e, 0xf1, 0xed, 0x4d, 0x92, 0xf2, 0x1f, 0xdb, + 0xc8, 0x5f, 0xd3, 0x0c, 0xa9, 0xbe, 0xaf, 0x37, 0x38, 0x62, 0x7a, 0x46, 0xbb, 0x19, 0xfa, 0x79, + 0xb9, 0x05, 0xbe, 0x2f, 0x08, 0x8b, 0x5a, 0xf2, 0x10, 0x66, 0xb7, 0x01, 0x00, 0x00, 0xff, 0xff, + 0x08, 0x2b, 0xe8, 0xb0, 0xcb, 0x02, 0x00, 0x00, +} + +func (m *GenesisState) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *GenesisState) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *GenesisState) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.Params.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + if len(m.Reports) > 0 { + for iNdEx := len(m.Reports) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Reports[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + } + if len(m.Reasons) > 0 { + for iNdEx := len(m.Reasons) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Reasons[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + } + if len(m.SubspacesData) > 0 { + for iNdEx := len(m.SubspacesData) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.SubspacesData[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *SubspaceDataEntry) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *SubspaceDataEntry) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SubspaceDataEntry) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.ReportID != 0 { + i = encodeVarintGenesis(dAtA, i, uint64(m.ReportID)) + i-- + dAtA[i] = 0x18 + } + if m.ReasonID != 0 { + i = encodeVarintGenesis(dAtA, i, uint64(m.ReasonID)) + i-- + dAtA[i] = 0x10 + } + if m.SubspaceID != 0 { + i = encodeVarintGenesis(dAtA, i, uint64(m.SubspaceID)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func encodeVarintGenesis(dAtA []byte, offset int, v uint64) int { + offset -= sovGenesis(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *GenesisState) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.SubspacesData) > 0 { + for _, e := range m.SubspacesData { + l = e.Size() + n += 1 + l + sovGenesis(uint64(l)) + } + } + if len(m.Reasons) > 0 { + for _, e := range m.Reasons { + l = e.Size() + n += 1 + l + sovGenesis(uint64(l)) + } + } + if len(m.Reports) > 0 { + for _, e := range m.Reports { + l = e.Size() + n += 1 + l + sovGenesis(uint64(l)) + } + } + l = m.Params.Size() + n += 1 + l + sovGenesis(uint64(l)) + return n +} + +func (m *SubspaceDataEntry) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.SubspaceID != 0 { + n += 1 + sovGenesis(uint64(m.SubspaceID)) + } + if m.ReasonID != 0 { + n += 1 + sovGenesis(uint64(m.ReasonID)) + } + if m.ReportID != 0 { + n += 1 + sovGenesis(uint64(m.ReportID)) + } + return n +} + +func sovGenesis(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozGenesis(x uint64) (n int) { + return sovGenesis(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *GenesisState) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: GenesisState: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: GenesisState: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SubspacesData", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenesis + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenesis + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.SubspacesData = append(m.SubspacesData, SubspaceDataEntry{}) + if err := m.SubspacesData[len(m.SubspacesData)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Reasons", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenesis + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenesis + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Reasons = append(m.Reasons, Reason{}) + if err := m.Reasons[len(m.Reasons)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Reports", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenesis + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenesis + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Reports = append(m.Reports, Report{}) + if err := m.Reports[len(m.Reports)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Params", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenesis + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenesis + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Params.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipGenesis(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthGenesis + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *SubspaceDataEntry) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: SubspaceDataEntry: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: SubspaceDataEntry: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field SubspaceID", wireType) + } + m.SubspaceID = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.SubspaceID |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ReasonID", wireType) + } + m.ReasonID = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.ReasonID |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ReportID", wireType) + } + m.ReportID = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.ReportID |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipGenesis(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthGenesis + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipGenesis(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowGenesis + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowGenesis + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowGenesis + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthGenesis + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupGenesis + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthGenesis + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthGenesis = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowGenesis = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupGenesis = fmt.Errorf("proto: unexpected end of group") +) diff --git a/x/reports/types/genesis_test.go b/x/reports/types/genesis_test.go new file mode 100644 index 0000000000..b4c9c1eef8 --- /dev/null +++ b/x/reports/types/genesis_test.go @@ -0,0 +1,174 @@ +package types_test + +import ( + "testing" + "time" + + "github.com/stretchr/testify/require" + + "github.com/desmos-labs/desmos/v3/x/reports/types" +) + +func TestValidateGenesis(t *testing.T) { + testCases := []struct { + name string + data *types.GenesisState + shouldErr bool + }{ + { + name: "duplicated subspaces data returns error", + data: types.NewGenesisState([]types.SubspaceDataEntry{ + types.NewSubspacesDataEntry(1, 1, 1), + types.NewSubspacesDataEntry(1, 1, 1), + }, nil, nil, types.DefaultParams()), + shouldErr: true, + }, + { + name: "invalid subspaces data returns error", + data: types.NewGenesisState([]types.SubspaceDataEntry{ + types.NewSubspacesDataEntry(0, 1, 1), + }, nil, nil, types.DefaultParams()), + shouldErr: true, + }, + { + name: "duplicated reason returns error", + data: types.NewGenesisState(nil, []types.Reason{ + types.NewReason(1, 1, "Spam", ""), + types.NewReason(1, 1, "Spam", ""), + }, nil, types.DefaultParams()), + shouldErr: true, + }, + { + name: "duplicated report returns error", + data: types.NewGenesisState(nil, nil, []types.Report{ + types.NewReport( + 1, + 1, + []uint32{1}, + "", + types.NewPostTarget(1), + "cosmos1atdl3cpms89md5qa3rxtql0drtgftch2zgkr7v", + time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC), + ), + types.NewReport( + 1, + 1, + []uint32{1}, + "", + types.NewPostTarget(1), + "cosmos1x85xq5m2ehkjzw928j9zfv3awdy0hqtnhrp9r6", + time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC), + ), + }, types.DefaultParams()), + shouldErr: true, + }, + { + name: "invalid report returns error", + data: types.NewGenesisState( + []types.SubspaceDataEntry{ + types.NewSubspacesDataEntry(1, 1, 2), + }, + nil, + []types.Report{ + types.NewReport( + 0, + 1, + []uint32{1}, + "", + types.NewPostTarget(1), + "cosmos1atdl3cpms89md5qa3rxtql0drtgftch2zgkr7v", + time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC), + ), + }, + types.DefaultParams(), + ), + shouldErr: true, + }, + { + name: "invalid params returns error", + data: types.NewGenesisState(nil, nil, nil, types.NewParams(types.NewStandardReasons( + types.NewStandardReason(0, "", ""), + ))), + shouldErr: true, + }, + { + name: "valid data returns no error", + data: types.NewGenesisState( + []types.SubspaceDataEntry{ + types.NewSubspacesDataEntry(1, 2, 2), + }, + []types.Reason{ + types.NewReason(1, 1, "Spam", ""), + }, + []types.Report{ + types.NewReport( + 1, + 1, + []uint32{1}, + "", + types.NewPostTarget(1), + "cosmos1atdl3cpms89md5qa3rxtql0drtgftch2zgkr7v", + time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC), + ), + }, + types.DefaultParams(), + ), + shouldErr: false, + }, + } + + for _, tc := range testCases { + tc := tc + t.Run(tc.name, func(t *testing.T) { + err := types.ValidateGenesis(tc.data) + if tc.shouldErr { + require.Error(t, err) + } else { + require.NoError(t, err) + } + }) + } + +} + +func TestSubspaceDataEntry_Validate(t *testing.T) { + testCases := []struct { + name string + data types.SubspaceDataEntry + shouldErr bool + }{ + { + name: "invalid subspace id returns error", + data: types.NewSubspacesDataEntry(0, 1, 1), + shouldErr: true, + }, + { + name: "invalid reason id returns error", + data: types.NewSubspacesDataEntry(1, 0, 1), + shouldErr: true, + }, + { + name: "invalid report id returns error", + data: types.NewSubspacesDataEntry(1, 1, 0), + shouldErr: true, + }, + { + name: "valid data returns no error", + data: types.NewSubspacesDataEntry(1, 1, 2), + shouldErr: false, + }, + } + + for _, tc := range testCases { + tc := tc + t.Run(tc.name, func(t *testing.T) { + err := tc.data.Validate() + if tc.shouldErr { + require.Error(t, err) + } else { + require.NoError(t, err) + } + }) + } + +} diff --git a/x/reports/types/hooks.go b/x/reports/types/hooks.go new file mode 100644 index 0000000000..05760f7dde --- /dev/null +++ b/x/reports/types/hooks.go @@ -0,0 +1,59 @@ +package types + +// DONTCOVER + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// Event Hooks +// These can be utilized to communicate between a reports keeper and another +// keeper which must take particular actions when reports/reasons change +// state. The second keeper must implement this interface, which then the +// reports keeper can call. + +// ReportsHooks event hooks for posts objects (noalias) +type ReportsHooks interface { + AfterReportSaved(ctx sdk.Context, subspaceID uint64, reportID uint64) // Must be called when a report is saved + AfterReportDeleted(ctx sdk.Context, subspaceID uint64, reportID uint64) // Must be called when a report is deleted + + AfterReasonSaved(ctx sdk.Context, subspaceID uint64, reasonID uint32) // Must be called when a reason is saved + AfterReasonDeleted(ctx sdk.Context, subspaceID uint64, reasonID uint32) // Must be called when a reason is deleted +} + +// -------------------------------------------------------------------------------------------------------------------- + +// MultiReportsHooks combines multiple subspaces hooks, all hook functions are run in array sequence +type MultiReportsHooks []ReportsHooks + +func NewMultiReportsHooks(hooks ...ReportsHooks) MultiReportsHooks { + return hooks +} + +// AfterReportSaved implements ReportsHooks +func (h MultiReportsHooks) AfterReportSaved(ctx sdk.Context, subspaceID uint64, reportID uint64) { + for _, hook := range h { + hook.AfterReportSaved(ctx, subspaceID, reportID) + } +} + +// AfterReportDeleted implements ReportsHooks +func (h MultiReportsHooks) AfterReportDeleted(ctx sdk.Context, subspaceID uint64, reportID uint64) { + for _, hook := range h { + hook.AfterReportDeleted(ctx, subspaceID, reportID) + } +} + +// AfterReasonSaved implements ReportsHooks +func (h MultiReportsHooks) AfterReasonSaved(ctx sdk.Context, subspaceID uint64, reasonID uint32) { + for _, hook := range h { + hook.AfterReasonSaved(ctx, subspaceID, reasonID) + } +} + +// AfterReasonDeleted implements ReportsHooks +func (h MultiReportsHooks) AfterReasonDeleted(ctx sdk.Context, subspaceID uint64, reasonID uint32) { + for _, hook := range h { + hook.AfterReasonDeleted(ctx, subspaceID, reasonID) + } +} diff --git a/x/reports/types/keys.go b/x/reports/types/keys.go new file mode 100644 index 0000000000..5f4482ee09 --- /dev/null +++ b/x/reports/types/keys.go @@ -0,0 +1,117 @@ +package types + +// DONTCOVER + +import ( + "encoding/binary" + + poststypes "github.com/desmos-labs/desmos/v3/x/posts/types" + + subspacestypes "github.com/desmos-labs/desmos/v3/x/subspaces/types" +) + +const ( + ModuleName = "reports" + RouterKey = ModuleName + StoreKey = ModuleName + QuerierRoute = ModuleName + + ActionCreateReport = "create_report" + ActionDeleteReport = "delete_report" + ActionSupportStandardReason = "support_standard_reason" + ActionAddReason = "add_reason" + ActionRemoveReason = "remove_reason" +) + +var ( + NextReportIDPrefix = []byte{0x01} + ReportPrefix = []byte{0x02} + PostsReportsPrefix = []byte{0x03} + UsersReportsPrefix = []byte{0x04} + + NextReasonIDPrefix = []byte{0x10} + ReasonPrefix = []byte{0x11} +) + +// GetReportIDBytes returns the byte representation of the reportID +func GetReportIDBytes(reportID uint64) (reportIDBz []byte) { + reportIDBz = make([]byte, 8) + binary.BigEndian.PutUint64(reportIDBz, reportID) + return reportIDBz +} + +// GetReportIDFromBytes returns reportID in uint64 format from a byte array +func GetReportIDFromBytes(bz []byte) (reportID uint64) { + return binary.BigEndian.Uint64(bz) +} + +// NextReportIDStoreKey returns the key used to store the next report id for the given subspace +func NextReportIDStoreKey(subspaceID uint64) []byte { + return append(NextReportIDPrefix, subspacestypes.GetSubspaceIDBytes(subspaceID)...) +} + +// SubspaceReportsPrefix returns the store prefix used to store all the reports related to the given subspace +func SubspaceReportsPrefix(subspaceID uint64) []byte { + return append(ReportPrefix, subspacestypes.GetSubspaceIDBytes(subspaceID)...) +} + +// ReportStoreKey returns the key used to store the report with the given subspace id and report id +func ReportStoreKey(subspaceID uint64, reportID uint64) []byte { + return append(SubspaceReportsPrefix(subspaceID), GetReportIDBytes(reportID)...) +} + +// PostReportsPrefix returns the prefix used to store the references of the reports for the given post +func PostReportsPrefix(subspaceID uint64, postID uint64) []byte { + postsReportsSuffix := append(subspacestypes.GetSubspaceIDBytes(subspaceID), poststypes.GetPostIDBytes(postID)...) + return append(PostsReportsPrefix, postsReportsSuffix...) +} + +// PostReportStoreKey returns the key used to store the reference to a report for the post from the given reporter +func PostReportStoreKey(subspaceID uint64, postID uint64, reporter string) []byte { + return append(PostReportsPrefix(subspaceID, postID), GetUserAddressBytes(reporter)...) +} + +// GetUserAddressBytes returns the byte representation of the given user address +func GetUserAddressBytes(address string) []byte { + return []byte(address) +} + +// UserReportsPrefix returns the prefix used to store the reports from the given reporter +func UserReportsPrefix(subspaceID uint64, user string) []byte { + userReportsSuffix := append(subspacestypes.GetSubspaceIDBytes(subspaceID), GetUserAddressBytes(user)...) + return append(UsersReportsPrefix, userReportsSuffix...) +} + +// UserReportStoreKey returns the key used to store the report for the given user having the given id +func UserReportStoreKey(subspaceID uint64, user string, reporter string) []byte { + return append(UserReportsPrefix(subspaceID, user), GetUserAddressBytes(reporter)...) +} + +// -------------------------------------------------------------------------------------------------------------------- + +// GetReasonIDBytes returns the byte representation of the reasonID +func GetReasonIDBytes(reasonID uint32) (reasonIDBz []byte) { + reasonIDBz = make([]byte, 4) + binary.BigEndian.PutUint32(reasonIDBz, reasonID) + return reasonIDBz +} + +// GetReasonIDFromBytes returns reasonID in uint32 format from a byte array +func GetReasonIDFromBytes(bz []byte) (reasonID uint32) { + return binary.BigEndian.Uint32(bz) +} + +// NextReasonIDStoreKey returns the key used to store the next reason id for the given subspace +func NextReasonIDStoreKey(subspaceID uint64) []byte { + return append(NextReasonIDPrefix, subspacestypes.GetSubspaceIDBytes(subspaceID)...) +} + +// SubspaceReasonsPrefix returns the store prefix used to store all the reports for the given subspace +func SubspaceReasonsPrefix(subspaceID uint64) []byte { + return append(ReasonPrefix, subspacestypes.GetSubspaceIDBytes(subspaceID)...) +} + +// ReasonStoreKey returns the key used to store the reason with the given subspace id and reason id +func ReasonStoreKey(subspaceID uint64, reasonID uint32) []byte { + return append(SubspaceReasonsPrefix(subspaceID), GetReasonIDBytes(reasonID)...) +} diff --git a/x/reports/types/models.go b/x/reports/types/models.go new file mode 100644 index 0000000000..caf3d7c30d --- /dev/null +++ b/x/reports/types/models.go @@ -0,0 +1,217 @@ +package types + +import ( + "fmt" + "strconv" + "strings" + "time" + + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/gogo/protobuf/proto" +) + +// ParseReportID parses the given value as a report id, returning an error if it's invalid +func ParseReportID(value string) (uint64, error) { + if value == "" { + return 0, nil + } + + reportID, err := strconv.ParseUint(value, 10, 64) + if err != nil { + return 0, fmt.Errorf("invalid report id: %s", err) + } + return reportID, nil +} + +// NewReport returns a new Report instance +func NewReport( + subspaceID uint64, + id uint64, + reasonsIDs []uint32, + message string, + target ReportTarget, + reporter string, + creationDate time.Time, +) Report { + targetAny, err := codectypes.NewAnyWithValue(target) + if err != nil { + panic("failed to pack target to any type") + } + + return Report{ + SubspaceID: subspaceID, + ID: id, + ReasonsIDs: reasonsIDs, + Message: message, + Target: targetAny, + Reporter: reporter, + CreationDate: creationDate, + } +} + +// Validate implements fmt.Validator +func (r Report) Validate() error { + if r.SubspaceID == 0 { + return fmt.Errorf("invalid subspace id: %d", r.SubspaceID) + } + + if r.ID == 0 { + return fmt.Errorf("invalid report id: %d", r.ID) + } + + if len(r.ReasonsIDs) == 0 { + return fmt.Errorf("reasons ids cannot be empty") + } + + for _, reasonID := range r.ReasonsIDs { + if reasonID == 0 { + return fmt.Errorf("invalid reason id: %d", reasonID) + } + } + + _, err := sdk.AccAddressFromBech32(r.Reporter) + if err != nil { + return fmt.Errorf("invalid reporter address: %s", err) + } + + err = r.Target.GetCachedValue().(ReportTarget).Validate() + if err != nil { + return err + } + + if r.CreationDate.IsZero() { + return fmt.Errorf("invalid report creation date: %s", r.CreationDate) + } + + return nil +} + +// UnpackInterfaces implements codectypes.UnpackInterfacesMessage +func (r *Report) UnpackInterfaces(unpacker codectypes.AnyUnpacker) error { + var target ReportTarget + return unpacker.UnpackAny(r.Target, &target) +} + +// -------------------------------------------------------------------------------------------------------------------- + +// ReportTarget represents a generic report target +type ReportTarget interface { + proto.Message + + isReportData() + Validate() error +} + +// -------------------------------------------------------------------------------------------------------------------- + +var _ ReportTarget = &UserTarget{} + +// NewUserTarget returns a new UserTarget instance +func NewUserTarget(user string) *UserTarget { + return &UserTarget{ + User: user, + } +} + +// isReportData implements ReportTarget +func (t *UserTarget) isReportData() {} + +// Validate implements ReportTarget +func (t *UserTarget) Validate() error { + // We don't check the validity against sdk.AccAddress because the reported address might be another chain account + if strings.TrimSpace(t.User) == "" { + return fmt.Errorf("invalid reported user: %s", t.User) + } + + return nil +} + +// -------------------------------------------------------------------------------------------------------------------- + +var _ ReportTarget = &PostTarget{} + +// NewPostTarget returns a new PostTarget instance +func NewPostTarget(postID uint64) *PostTarget { + return &PostTarget{ + PostID: postID, + } +} + +// isReportData implements ReportTarget +func (t *PostTarget) isReportData() {} + +// Validate implements ReportTarget +func (t *PostTarget) Validate() error { + if t.PostID == 0 { + return fmt.Errorf("invalid post id: %d", t.PostID) + } + + return nil +} + +// -------------------------------------------------------------------------------------------------------------------- + +// ParseReasonID parses the given value as a reason id, returning an error if it's invalid +func ParseReasonID(value string) (uint32, error) { + if value == "" { + return 0, nil + } + + reasonID, err := strconv.ParseUint(value, 10, 32) + if err != nil { + return 0, fmt.Errorf("invalid reason id: %s", err) + } + return uint32(reasonID), nil +} + +// ParseReasonsIDs parses the given comma-separated values as a list of reasons ids. +func ParseReasonsIDs(value string) ([]uint32, error) { + strValues := strings.Split(value, ",") + reasons := make([]uint32, len(strValues)) + for i, str := range strValues { + reason, err := ParseReasonID(str) + if err != nil { + return nil, err + } + reasons[i] = reason + } + return reasons, nil +} + +// ContainsReason returns true iff the given reasons contain the provided reasonID +func ContainsReason(reasons []uint32, reasonID uint32) bool { + for _, reason := range reasons { + if reason == reasonID { + return true + } + } + return false +} + +// NewReason returns a new Reason instance +func NewReason(subspaceID uint64, id uint32, title string, description string) Reason { + return Reason{ + SubspaceID: subspaceID, + ID: id, + Title: title, + Description: description, + } +} + +// Validate implements fmt.Validator +func (r Reason) Validate() error { + if r.SubspaceID == 0 { + return fmt.Errorf("invalid subspace id: %d", r.SubspaceID) + } + + if r.ID == 0 { + return fmt.Errorf("invalid reason id: %d", r.ID) + } + + if strings.TrimSpace(r.Title) == "" { + return fmt.Errorf("invalid reason title: %s", r.Title) + } + + return nil +} diff --git a/x/reports/types/models.pb.go b/x/reports/types/models.pb.go new file mode 100644 index 0000000000..ed5eb0cfc8 --- /dev/null +++ b/x/reports/types/models.pb.go @@ -0,0 +1,1957 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: desmos/reports/v1/models.proto + +package types + +import ( + fmt "fmt" + types "github.com/cosmos/cosmos-sdk/codec/types" + _ "github.com/gogo/protobuf/gogoproto" + proto "github.com/gogo/protobuf/proto" + github_com_gogo_protobuf_types "github.com/gogo/protobuf/types" + _ "github.com/regen-network/cosmos-proto" + _ "google.golang.org/protobuf/types/known/timestamppb" + io "io" + math "math" + math_bits "math/bits" + time "time" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf +var _ = time.Kitchen + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +// Report contains the data of a generic report +type Report struct { + // Id of the subspace for which the report has been created + SubspaceID uint64 `protobuf:"varint,1,opt,name=subspace_id,json=subspaceId,proto3" json:"subspace_id,omitempty" yaml:"subspace_id"` + // Id of the report + ID uint64 `protobuf:"varint,2,opt,name=id,proto3" json:"id,omitempty" yaml:"id"` + // Id of the reason this report has been created for + ReasonsIDs []uint32 `protobuf:"varint,3,rep,packed,name=reasons_ids,json=reasonsIds,proto3" json:"reasons_ids,omitempty" yaml:"reasons_ids"` + // (optional) Message attached to this report + Message string `protobuf:"bytes,4,opt,name=message,proto3" json:"message,omitempty" yaml:"message"` + // Address of the reporter + Reporter string `protobuf:"bytes,5,opt,name=reporter,proto3" json:"reporter,omitempty" yaml:"reporter"` + // Target of the report + Target *types.Any `protobuf:"bytes,6,opt,name=target,proto3" json:"target,omitempty" yaml:"target"` + // Time in which the report was created + CreationDate time.Time `protobuf:"bytes,7,opt,name=creation_date,json=creationDate,proto3,stdtime" json:"creation_date" yaml:"creation_date"` +} + +func (m *Report) Reset() { *m = Report{} } +func (m *Report) String() string { return proto.CompactTextString(m) } +func (*Report) ProtoMessage() {} +func (*Report) Descriptor() ([]byte, []int) { + return fileDescriptor_908cff0b05e14c5f, []int{0} +} +func (m *Report) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Report) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Report.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Report) XXX_Merge(src proto.Message) { + xxx_messageInfo_Report.Merge(m, src) +} +func (m *Report) XXX_Size() int { + return m.Size() +} +func (m *Report) XXX_DiscardUnknown() { + xxx_messageInfo_Report.DiscardUnknown(m) +} + +var xxx_messageInfo_Report proto.InternalMessageInfo + +func (m *Report) GetSubspaceID() uint64 { + if m != nil { + return m.SubspaceID + } + return 0 +} + +func (m *Report) GetID() uint64 { + if m != nil { + return m.ID + } + return 0 +} + +func (m *Report) GetReasonsIDs() []uint32 { + if m != nil { + return m.ReasonsIDs + } + return nil +} + +func (m *Report) GetMessage() string { + if m != nil { + return m.Message + } + return "" +} + +func (m *Report) GetReporter() string { + if m != nil { + return m.Reporter + } + return "" +} + +func (m *Report) GetTarget() *types.Any { + if m != nil { + return m.Target + } + return nil +} + +func (m *Report) GetCreationDate() time.Time { + if m != nil { + return m.CreationDate + } + return time.Time{} +} + +// UserTarget contains the data of a report about a user +type UserTarget struct { + // Address of the reported user + User string `protobuf:"bytes,1,opt,name=user,proto3" json:"user,omitempty" yaml:"user"` +} + +func (m *UserTarget) Reset() { *m = UserTarget{} } +func (m *UserTarget) String() string { return proto.CompactTextString(m) } +func (*UserTarget) ProtoMessage() {} +func (*UserTarget) Descriptor() ([]byte, []int) { + return fileDescriptor_908cff0b05e14c5f, []int{1} +} +func (m *UserTarget) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *UserTarget) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_UserTarget.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *UserTarget) XXX_Merge(src proto.Message) { + xxx_messageInfo_UserTarget.Merge(m, src) +} +func (m *UserTarget) XXX_Size() int { + return m.Size() +} +func (m *UserTarget) XXX_DiscardUnknown() { + xxx_messageInfo_UserTarget.DiscardUnknown(m) +} + +var xxx_messageInfo_UserTarget proto.InternalMessageInfo + +func (m *UserTarget) GetUser() string { + if m != nil { + return m.User + } + return "" +} + +// PostTarget contains the data of a report about a post +type PostTarget struct { + // Id of the reported post + PostID uint64 `protobuf:"varint,1,opt,name=post_id,json=postId,proto3" json:"post_id,omitempty" yaml:"post_id"` +} + +func (m *PostTarget) Reset() { *m = PostTarget{} } +func (m *PostTarget) String() string { return proto.CompactTextString(m) } +func (*PostTarget) ProtoMessage() {} +func (*PostTarget) Descriptor() ([]byte, []int) { + return fileDescriptor_908cff0b05e14c5f, []int{2} +} +func (m *PostTarget) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *PostTarget) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_PostTarget.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *PostTarget) XXX_Merge(src proto.Message) { + xxx_messageInfo_PostTarget.Merge(m, src) +} +func (m *PostTarget) XXX_Size() int { + return m.Size() +} +func (m *PostTarget) XXX_DiscardUnknown() { + xxx_messageInfo_PostTarget.DiscardUnknown(m) +} + +var xxx_messageInfo_PostTarget proto.InternalMessageInfo + +func (m *PostTarget) GetPostID() uint64 { + if m != nil { + return m.PostID + } + return 0 +} + +// Reason contains the data about a reporting reason +type Reason struct { + // Id of the subspace for which this reason is valid + SubspaceID uint64 `protobuf:"varint,1,opt,name=subspace_id,json=subspaceId,proto3" json:"subspace_id,omitempty" yaml:"subspace_id"` + // Id of the reason inside the subspace + ID uint32 `protobuf:"varint,2,opt,name=id,proto3" json:"id,omitempty" yaml:"id"` + // Title of the reason + Title string `protobuf:"bytes,3,opt,name=title,proto3" json:"title,omitempty" yaml:"title"` + // (optional) Extended description of the reason and the cases it applies to + Description string `protobuf:"bytes,4,opt,name=description,proto3" json:"description,omitempty" yaml:"description"` +} + +func (m *Reason) Reset() { *m = Reason{} } +func (m *Reason) String() string { return proto.CompactTextString(m) } +func (*Reason) ProtoMessage() {} +func (*Reason) Descriptor() ([]byte, []int) { + return fileDescriptor_908cff0b05e14c5f, []int{3} +} +func (m *Reason) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Reason) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Reason.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Reason) XXX_Merge(src proto.Message) { + xxx_messageInfo_Reason.Merge(m, src) +} +func (m *Reason) XXX_Size() int { + return m.Size() +} +func (m *Reason) XXX_DiscardUnknown() { + xxx_messageInfo_Reason.DiscardUnknown(m) +} + +var xxx_messageInfo_Reason proto.InternalMessageInfo + +func (m *Reason) GetSubspaceID() uint64 { + if m != nil { + return m.SubspaceID + } + return 0 +} + +func (m *Reason) GetID() uint32 { + if m != nil { + return m.ID + } + return 0 +} + +func (m *Reason) GetTitle() string { + if m != nil { + return m.Title + } + return "" +} + +func (m *Reason) GetDescription() string { + if m != nil { + return m.Description + } + return "" +} + +// Params contains the module parameters +type Params struct { + // List of available reasons from which new subspaces can pick their default + // ones + StandardReasons []StandardReason `protobuf:"bytes,1,rep,name=standard_reasons,json=standardReasons,proto3" json:"standard_reasons" yaml:"standard_reasons"` +} + +func (m *Params) Reset() { *m = Params{} } +func (m *Params) String() string { return proto.CompactTextString(m) } +func (*Params) ProtoMessage() {} +func (*Params) Descriptor() ([]byte, []int) { + return fileDescriptor_908cff0b05e14c5f, []int{4} +} +func (m *Params) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Params) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Params.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Params) XXX_Merge(src proto.Message) { + xxx_messageInfo_Params.Merge(m, src) +} +func (m *Params) XXX_Size() int { + return m.Size() +} +func (m *Params) XXX_DiscardUnknown() { + xxx_messageInfo_Params.DiscardUnknown(m) +} + +var xxx_messageInfo_Params proto.InternalMessageInfo + +func (m *Params) GetStandardReasons() []StandardReason { + if m != nil { + return m.StandardReasons + } + return nil +} + +// StandardReason contains the data of a standard reason that can be picked and +// used from different subspaces +type StandardReason struct { + // Id of the reason inside the subspace + ID uint32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty" yaml:"id"` + // Title of the reason + Title string `protobuf:"bytes,2,opt,name=title,proto3" json:"title,omitempty" yaml:"title"` + // (optional) Extended description of the reason and the cases it applies to + Description string `protobuf:"bytes,3,opt,name=description,proto3" json:"description,omitempty" yaml:"description"` +} + +func (m *StandardReason) Reset() { *m = StandardReason{} } +func (m *StandardReason) String() string { return proto.CompactTextString(m) } +func (*StandardReason) ProtoMessage() {} +func (*StandardReason) Descriptor() ([]byte, []int) { + return fileDescriptor_908cff0b05e14c5f, []int{5} +} +func (m *StandardReason) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *StandardReason) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_StandardReason.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *StandardReason) XXX_Merge(src proto.Message) { + xxx_messageInfo_StandardReason.Merge(m, src) +} +func (m *StandardReason) XXX_Size() int { + return m.Size() +} +func (m *StandardReason) XXX_DiscardUnknown() { + xxx_messageInfo_StandardReason.DiscardUnknown(m) +} + +var xxx_messageInfo_StandardReason proto.InternalMessageInfo + +func (m *StandardReason) GetID() uint32 { + if m != nil { + return m.ID + } + return 0 +} + +func (m *StandardReason) GetTitle() string { + if m != nil { + return m.Title + } + return "" +} + +func (m *StandardReason) GetDescription() string { + if m != nil { + return m.Description + } + return "" +} + +func init() { + proto.RegisterType((*Report)(nil), "desmos.reports.v1.Report") + proto.RegisterType((*UserTarget)(nil), "desmos.reports.v1.UserTarget") + proto.RegisterType((*PostTarget)(nil), "desmos.reports.v1.PostTarget") + proto.RegisterType((*Reason)(nil), "desmos.reports.v1.Reason") + proto.RegisterType((*Params)(nil), "desmos.reports.v1.Params") + proto.RegisterType((*StandardReason)(nil), "desmos.reports.v1.StandardReason") +} + +func init() { proto.RegisterFile("desmos/reports/v1/models.proto", fileDescriptor_908cff0b05e14c5f) } + +var fileDescriptor_908cff0b05e14c5f = []byte{ + // 656 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x54, 0x3d, 0x6f, 0xd3, 0x4e, + 0x18, 0xcf, 0x25, 0xad, 0xdb, 0x5e, 0x9a, 0xbe, 0xb8, 0xd5, 0xff, 0x6f, 0x2a, 0xe4, 0x0b, 0x57, + 0x84, 0x32, 0x80, 0x4d, 0x5b, 0x21, 0xa1, 0x32, 0x20, 0xac, 0x32, 0x04, 0x09, 0xa9, 0x72, 0xcb, + 0xc2, 0x12, 0x5d, 0x72, 0x87, 0xb1, 0x14, 0xe7, 0x2c, 0xdf, 0xa5, 0x22, 0x23, 0xdf, 0xa0, 0x23, + 0x63, 0xf9, 0x36, 0x1d, 0x3b, 0x32, 0x99, 0x2a, 0x5d, 0x98, 0xfd, 0x09, 0x90, 0x7d, 0xe7, 0xd6, + 0x69, 0x85, 0x04, 0x88, 0xed, 0xee, 0xf9, 0xbd, 0x3c, 0xf6, 0xef, 0x79, 0x6c, 0x68, 0x53, 0x26, + 0x22, 0x2e, 0xdc, 0x84, 0xc5, 0x3c, 0x91, 0xc2, 0x3d, 0xd9, 0x71, 0x23, 0x4e, 0xd9, 0x50, 0x38, + 0x71, 0xc2, 0x25, 0x37, 0xd7, 0x15, 0xee, 0x68, 0xdc, 0x39, 0xd9, 0xd9, 0xda, 0x0c, 0x78, 0xc0, + 0x0b, 0xd4, 0xcd, 0x4f, 0x8a, 0xb8, 0x75, 0x2f, 0xe0, 0x3c, 0x18, 0x32, 0xb7, 0xb8, 0xf5, 0xc7, + 0x1f, 0x5c, 0x32, 0x9a, 0x68, 0x08, 0xdd, 0x86, 0x64, 0x18, 0x31, 0x21, 0x49, 0x14, 0x97, 0xda, + 0x01, 0xcf, 0x9b, 0xf4, 0x94, 0xa9, 0xba, 0x28, 0x08, 0x9f, 0x37, 0xa0, 0xe1, 0x17, 0xbd, 0xcd, + 0xd7, 0xb0, 0x29, 0xc6, 0x7d, 0x11, 0x93, 0x01, 0xeb, 0x85, 0xd4, 0x02, 0x6d, 0xd0, 0x99, 0xf3, + 0x1e, 0x4e, 0x53, 0x04, 0x8f, 0x74, 0xb9, 0x7b, 0x90, 0xa5, 0xc8, 0x9c, 0x90, 0x68, 0xb8, 0x8f, + 0x2b, 0x54, 0xec, 0xc3, 0xf2, 0xd6, 0xa5, 0xe6, 0x36, 0xac, 0x87, 0xd4, 0xaa, 0x17, 0xea, 0x8d, + 0x69, 0x8a, 0xea, 0x85, 0x6a, 0x49, 0xa9, 0x72, 0x72, 0x3d, 0xa4, 0x79, 0xaf, 0x84, 0x11, 0xc1, + 0x47, 0xa2, 0x17, 0x52, 0x61, 0x35, 0xda, 0x8d, 0x4e, 0x4b, 0xf5, 0xf2, 0x55, 0xb9, 0x7b, 0x20, + 0x6e, 0x7a, 0x55, 0xa8, 0xd8, 0x87, 0xfa, 0xd6, 0xa5, 0xc2, 0x7c, 0x0c, 0x17, 0x22, 0x26, 0x04, + 0x09, 0x98, 0x35, 0xd7, 0x06, 0x9d, 0x25, 0xcf, 0xcc, 0x52, 0xb4, 0xa2, 0x44, 0x1a, 0xc0, 0x7e, + 0x49, 0x31, 0x5d, 0xb8, 0xa8, 0x62, 0x66, 0x89, 0x35, 0x5f, 0xd0, 0x37, 0xb2, 0x14, 0xad, 0x96, + 0x3d, 0x14, 0x82, 0xfd, 0x6b, 0x92, 0xf9, 0x12, 0x1a, 0x92, 0x24, 0x01, 0x93, 0x96, 0xd1, 0x06, + 0x9d, 0xe6, 0xee, 0xa6, 0xa3, 0x92, 0x76, 0xca, 0xa4, 0x9d, 0x57, 0xa3, 0x89, 0xb7, 0x9e, 0xa5, + 0xa8, 0xa5, 0x4c, 0x14, 0x1b, 0xfb, 0x5a, 0x66, 0x12, 0xd8, 0x1a, 0x24, 0x8c, 0xc8, 0x90, 0x8f, + 0x7a, 0x94, 0x48, 0x66, 0x2d, 0x14, 0x3e, 0x5b, 0x77, 0x7c, 0x8e, 0xcb, 0x89, 0x79, 0xed, 0xf3, + 0x14, 0xd5, 0xb2, 0x14, 0x6d, 0x2a, 0xc7, 0x19, 0x39, 0x3e, 0xfd, 0x8e, 0x80, 0xbf, 0x5c, 0xd6, + 0x0e, 0x88, 0x64, 0xfb, 0x8b, 0x5f, 0xce, 0x10, 0xf8, 0x71, 0x86, 0x00, 0x7e, 0x01, 0xe1, 0x3b, + 0xc1, 0x92, 0x63, 0xd5, 0x7a, 0x1b, 0xce, 0x8d, 0x05, 0x4b, 0x8a, 0x31, 0x2e, 0x79, 0xab, 0x59, + 0x8a, 0x9a, 0xca, 0x31, 0xaf, 0x62, 0xbf, 0x00, 0x2b, 0xe2, 0xb7, 0x10, 0x1e, 0x72, 0x21, 0xb5, + 0xf8, 0x19, 0x5c, 0x88, 0xb9, 0x90, 0x37, 0x6b, 0x70, 0x7f, 0x9a, 0x22, 0x23, 0x27, 0x14, 0xc3, + 0xd4, 0x09, 0x6b, 0x0a, 0xf6, 0x8d, 0xfc, 0xd4, 0xa5, 0x15, 0xbb, 0x4b, 0x90, 0xaf, 0x55, 0x3e, + 0xa7, 0x7f, 0xbf, 0x56, 0xad, 0x5f, 0xaf, 0xd5, 0x23, 0x38, 0x2f, 0x43, 0x39, 0x64, 0x56, 0xa3, + 0x78, 0xeb, 0xb5, 0x2c, 0x45, 0xcb, 0x7a, 0x32, 0x79, 0x19, 0xfb, 0x0a, 0x36, 0x9f, 0xc3, 0x26, + 0x65, 0x62, 0x90, 0x84, 0x71, 0x9e, 0xa3, 0xde, 0x9d, 0xff, 0x6e, 0x9e, 0xa2, 0x02, 0x62, 0xbf, + 0x4a, 0xad, 0xbc, 0xe2, 0x67, 0x00, 0x8d, 0x43, 0x92, 0x90, 0x48, 0x98, 0x11, 0x5c, 0x13, 0x92, + 0x8c, 0x28, 0x49, 0x68, 0x4f, 0x6f, 0xa7, 0x05, 0xda, 0x8d, 0x4e, 0x73, 0xf7, 0x81, 0x73, 0xe7, + 0xfb, 0x76, 0x8e, 0x34, 0x55, 0xe5, 0xe3, 0x21, 0x3d, 0xf0, 0xff, 0x75, 0x00, 0xb7, 0x8c, 0xb0, + 0xbf, 0x2a, 0x66, 0x04, 0xa2, 0xf2, 0x0c, 0x5f, 0x01, 0x5c, 0x99, 0xb5, 0xd3, 0x39, 0x81, 0xdf, + 0xcc, 0xa9, 0xfe, 0x47, 0x39, 0x35, 0xfe, 0x22, 0x27, 0xef, 0xcd, 0xf9, 0xd4, 0x06, 0x17, 0x53, + 0x1b, 0x5c, 0x4e, 0x6d, 0x70, 0x7a, 0x65, 0xd7, 0x2e, 0xae, 0xec, 0xda, 0xb7, 0x2b, 0xbb, 0xf6, + 0xfe, 0x69, 0x10, 0xca, 0x8f, 0xe3, 0xbe, 0x33, 0xe0, 0x91, 0xab, 0x62, 0x7a, 0x32, 0x24, 0x7d, + 0xa1, 0xcf, 0xee, 0xc9, 0x9e, 0xfb, 0xe9, 0xfa, 0xbf, 0x29, 0x27, 0x31, 0x13, 0x7d, 0xa3, 0xf8, + 0x60, 0xf6, 0x7e, 0x06, 0x00, 0x00, 0xff, 0xff, 0xfc, 0xab, 0xc8, 0x36, 0x56, 0x05, 0x00, 0x00, +} + +func (this *Report) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + that1, ok := that.(*Report) + if !ok { + that2, ok := that.(Report) + if ok { + that1 = &that2 + } else { + return false + } + } + if that1 == nil { + return this == nil + } else if this == nil { + return false + } + if this.SubspaceID != that1.SubspaceID { + return false + } + if this.ID != that1.ID { + return false + } + if len(this.ReasonsIDs) != len(that1.ReasonsIDs) { + return false + } + for i := range this.ReasonsIDs { + if this.ReasonsIDs[i] != that1.ReasonsIDs[i] { + return false + } + } + if this.Message != that1.Message { + return false + } + if this.Reporter != that1.Reporter { + return false + } + if !this.Target.Equal(that1.Target) { + return false + } + if !this.CreationDate.Equal(that1.CreationDate) { + return false + } + return true +} +func (this *UserTarget) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + that1, ok := that.(*UserTarget) + if !ok { + that2, ok := that.(UserTarget) + if ok { + that1 = &that2 + } else { + return false + } + } + if that1 == nil { + return this == nil + } else if this == nil { + return false + } + if this.User != that1.User { + return false + } + return true +} +func (this *PostTarget) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + that1, ok := that.(*PostTarget) + if !ok { + that2, ok := that.(PostTarget) + if ok { + that1 = &that2 + } else { + return false + } + } + if that1 == nil { + return this == nil + } else if this == nil { + return false + } + if this.PostID != that1.PostID { + return false + } + return true +} +func (this *Reason) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + that1, ok := that.(*Reason) + if !ok { + that2, ok := that.(Reason) + if ok { + that1 = &that2 + } else { + return false + } + } + if that1 == nil { + return this == nil + } else if this == nil { + return false + } + if this.SubspaceID != that1.SubspaceID { + return false + } + if this.ID != that1.ID { + return false + } + if this.Title != that1.Title { + return false + } + if this.Description != that1.Description { + return false + } + return true +} +func (this *Params) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + that1, ok := that.(*Params) + if !ok { + that2, ok := that.(Params) + if ok { + that1 = &that2 + } else { + return false + } + } + if that1 == nil { + return this == nil + } else if this == nil { + return false + } + if len(this.StandardReasons) != len(that1.StandardReasons) { + return false + } + for i := range this.StandardReasons { + if !this.StandardReasons[i].Equal(&that1.StandardReasons[i]) { + return false + } + } + return true +} +func (this *StandardReason) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + that1, ok := that.(*StandardReason) + if !ok { + that2, ok := that.(StandardReason) + if ok { + that1 = &that2 + } else { + return false + } + } + if that1 == nil { + return this == nil + } else if this == nil { + return false + } + if this.ID != that1.ID { + return false + } + if this.Title != that1.Title { + return false + } + if this.Description != that1.Description { + return false + } + return true +} +func (m *Report) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Report) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Report) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + n1, err1 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.CreationDate, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.CreationDate):]) + if err1 != nil { + return 0, err1 + } + i -= n1 + i = encodeVarintModels(dAtA, i, uint64(n1)) + i-- + dAtA[i] = 0x3a + if m.Target != nil { + { + size, err := m.Target.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintModels(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x32 + } + if len(m.Reporter) > 0 { + i -= len(m.Reporter) + copy(dAtA[i:], m.Reporter) + i = encodeVarintModels(dAtA, i, uint64(len(m.Reporter))) + i-- + dAtA[i] = 0x2a + } + if len(m.Message) > 0 { + i -= len(m.Message) + copy(dAtA[i:], m.Message) + i = encodeVarintModels(dAtA, i, uint64(len(m.Message))) + i-- + dAtA[i] = 0x22 + } + if len(m.ReasonsIDs) > 0 { + dAtA4 := make([]byte, len(m.ReasonsIDs)*10) + var j3 int + for _, num := range m.ReasonsIDs { + for num >= 1<<7 { + dAtA4[j3] = uint8(uint64(num)&0x7f | 0x80) + num >>= 7 + j3++ + } + dAtA4[j3] = uint8(num) + j3++ + } + i -= j3 + copy(dAtA[i:], dAtA4[:j3]) + i = encodeVarintModels(dAtA, i, uint64(j3)) + i-- + dAtA[i] = 0x1a + } + if m.ID != 0 { + i = encodeVarintModels(dAtA, i, uint64(m.ID)) + i-- + dAtA[i] = 0x10 + } + if m.SubspaceID != 0 { + i = encodeVarintModels(dAtA, i, uint64(m.SubspaceID)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *UserTarget) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *UserTarget) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *UserTarget) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.User) > 0 { + i -= len(m.User) + copy(dAtA[i:], m.User) + i = encodeVarintModels(dAtA, i, uint64(len(m.User))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *PostTarget) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *PostTarget) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *PostTarget) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.PostID != 0 { + i = encodeVarintModels(dAtA, i, uint64(m.PostID)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *Reason) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Reason) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Reason) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Description) > 0 { + i -= len(m.Description) + copy(dAtA[i:], m.Description) + i = encodeVarintModels(dAtA, i, uint64(len(m.Description))) + i-- + dAtA[i] = 0x22 + } + if len(m.Title) > 0 { + i -= len(m.Title) + copy(dAtA[i:], m.Title) + i = encodeVarintModels(dAtA, i, uint64(len(m.Title))) + i-- + dAtA[i] = 0x1a + } + if m.ID != 0 { + i = encodeVarintModels(dAtA, i, uint64(m.ID)) + i-- + dAtA[i] = 0x10 + } + if m.SubspaceID != 0 { + i = encodeVarintModels(dAtA, i, uint64(m.SubspaceID)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *Params) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Params) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Params) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.StandardReasons) > 0 { + for iNdEx := len(m.StandardReasons) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.StandardReasons[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintModels(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *StandardReason) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *StandardReason) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *StandardReason) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Description) > 0 { + i -= len(m.Description) + copy(dAtA[i:], m.Description) + i = encodeVarintModels(dAtA, i, uint64(len(m.Description))) + i-- + dAtA[i] = 0x1a + } + if len(m.Title) > 0 { + i -= len(m.Title) + copy(dAtA[i:], m.Title) + i = encodeVarintModels(dAtA, i, uint64(len(m.Title))) + i-- + dAtA[i] = 0x12 + } + if m.ID != 0 { + i = encodeVarintModels(dAtA, i, uint64(m.ID)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func encodeVarintModels(dAtA []byte, offset int, v uint64) int { + offset -= sovModels(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *Report) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.SubspaceID != 0 { + n += 1 + sovModels(uint64(m.SubspaceID)) + } + if m.ID != 0 { + n += 1 + sovModels(uint64(m.ID)) + } + if len(m.ReasonsIDs) > 0 { + l = 0 + for _, e := range m.ReasonsIDs { + l += sovModels(uint64(e)) + } + n += 1 + sovModels(uint64(l)) + l + } + l = len(m.Message) + if l > 0 { + n += 1 + l + sovModels(uint64(l)) + } + l = len(m.Reporter) + if l > 0 { + n += 1 + l + sovModels(uint64(l)) + } + if m.Target != nil { + l = m.Target.Size() + n += 1 + l + sovModels(uint64(l)) + } + l = github_com_gogo_protobuf_types.SizeOfStdTime(m.CreationDate) + n += 1 + l + sovModels(uint64(l)) + return n +} + +func (m *UserTarget) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.User) + if l > 0 { + n += 1 + l + sovModels(uint64(l)) + } + return n +} + +func (m *PostTarget) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.PostID != 0 { + n += 1 + sovModels(uint64(m.PostID)) + } + return n +} + +func (m *Reason) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.SubspaceID != 0 { + n += 1 + sovModels(uint64(m.SubspaceID)) + } + if m.ID != 0 { + n += 1 + sovModels(uint64(m.ID)) + } + l = len(m.Title) + if l > 0 { + n += 1 + l + sovModels(uint64(l)) + } + l = len(m.Description) + if l > 0 { + n += 1 + l + sovModels(uint64(l)) + } + return n +} + +func (m *Params) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.StandardReasons) > 0 { + for _, e := range m.StandardReasons { + l = e.Size() + n += 1 + l + sovModels(uint64(l)) + } + } + return n +} + +func (m *StandardReason) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.ID != 0 { + n += 1 + sovModels(uint64(m.ID)) + } + l = len(m.Title) + if l > 0 { + n += 1 + l + sovModels(uint64(l)) + } + l = len(m.Description) + if l > 0 { + n += 1 + l + sovModels(uint64(l)) + } + return n +} + +func sovModels(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozModels(x uint64) (n int) { + return sovModels(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *Report) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowModels + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Report: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Report: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field SubspaceID", wireType) + } + m.SubspaceID = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowModels + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.SubspaceID |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ID", wireType) + } + m.ID = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowModels + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.ID |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType == 0 { + var v uint32 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowModels + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.ReasonsIDs = append(m.ReasonsIDs, v) + } else if wireType == 2 { + var packedLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowModels + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + packedLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if packedLen < 0 { + return ErrInvalidLengthModels + } + postIndex := iNdEx + packedLen + if postIndex < 0 { + return ErrInvalidLengthModels + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var elementCount int + var count int + for _, integer := range dAtA[iNdEx:postIndex] { + if integer < 128 { + count++ + } + } + elementCount = count + if elementCount != 0 && len(m.ReasonsIDs) == 0 { + m.ReasonsIDs = make([]uint32, 0, elementCount) + } + for iNdEx < postIndex { + var v uint32 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowModels + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.ReasonsIDs = append(m.ReasonsIDs, v) + } + } else { + return fmt.Errorf("proto: wrong wireType = %d for field ReasonsIDs", wireType) + } + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Message", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowModels + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthModels + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthModels + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Message = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Reporter", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowModels + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthModels + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthModels + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Reporter = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Target", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowModels + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthModels + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthModels + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Target == nil { + m.Target = &types.Any{} + } + if err := m.Target.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CreationDate", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowModels + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthModels + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthModels + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := github_com_gogo_protobuf_types.StdTimeUnmarshal(&m.CreationDate, dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipModels(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthModels + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *UserTarget) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowModels + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: UserTarget: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: UserTarget: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field User", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowModels + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthModels + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthModels + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.User = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipModels(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthModels + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *PostTarget) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowModels + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: PostTarget: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: PostTarget: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field PostID", wireType) + } + m.PostID = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowModels + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.PostID |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipModels(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthModels + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Reason) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowModels + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Reason: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Reason: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field SubspaceID", wireType) + } + m.SubspaceID = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowModels + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.SubspaceID |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ID", wireType) + } + m.ID = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowModels + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.ID |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Title", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowModels + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthModels + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthModels + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Title = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Description", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowModels + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthModels + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthModels + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Description = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipModels(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthModels + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Params) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowModels + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Params: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Params: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field StandardReasons", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowModels + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthModels + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthModels + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.StandardReasons = append(m.StandardReasons, StandardReason{}) + if err := m.StandardReasons[len(m.StandardReasons)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipModels(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthModels + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *StandardReason) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowModels + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: StandardReason: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: StandardReason: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ID", wireType) + } + m.ID = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowModels + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.ID |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Title", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowModels + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthModels + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthModels + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Title = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Description", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowModels + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthModels + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthModels + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Description = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipModels(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthModels + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipModels(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowModels + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowModels + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowModels + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthModels + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupModels + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthModels + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthModels = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowModels = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupModels = fmt.Errorf("proto: unexpected end of group") +) diff --git a/x/reports/types/models_test.go b/x/reports/types/models_test.go new file mode 100644 index 0000000000..c012dc7916 --- /dev/null +++ b/x/reports/types/models_test.go @@ -0,0 +1,247 @@ +package types_test + +import ( + "testing" + "time" + + "github.com/stretchr/testify/require" + + "github.com/desmos-labs/desmos/v3/x/reports/types" +) + +func TestReport_Validate(t *testing.T) { + testCases := []struct { + name string + report types.Report + shouldErr bool + }{ + { + name: "invalid subspace id returns error", + report: types.NewReport( + 0, + 1, + []uint32{1}, + "", + types.NewPostTarget(1), + "cosmos1atdl3cpms89md5qa3rxtql0drtgftch2zgkr7v", + time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC), + ), + shouldErr: true, + }, + { + name: "invalid id returns error", + report: types.NewReport( + 1, + 0, + []uint32{1}, + "", + types.NewPostTarget(1), + "cosmos1atdl3cpms89md5qa3rxtql0drtgftch2zgkr7v", + time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC), + ), + shouldErr: true, + }, + { + name: "empty reasons ids returns error", + report: types.NewReport( + 1, + 1, + nil, + "", + types.NewPostTarget(1), + "cosmos1atdl3cpms89md5qa3rxtql0drtgftch2zgkr7v", + time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC), + ), + shouldErr: true, + }, + { + name: "invalid reason id returns error", + report: types.NewReport( + 1, + 1, + []uint32{0}, + "", + types.NewPostTarget(1), + "cosmos1atdl3cpms89md5qa3rxtql0drtgftch2zgkr7v", + time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC), + ), + shouldErr: true, + }, + { + name: "invalid reporter returns error", + report: types.NewReport( + 1, + 1, + []uint32{1}, + "", + types.NewPostTarget(1), + "", + time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC), + ), + shouldErr: true, + }, + { + name: "invalid target returns error", + report: types.NewReport( + 1, + 1, + []uint32{1}, + "", + types.NewPostTarget(0), + "cosmos1atdl3cpms89md5qa3rxtql0drtgftch2zgkr7v", + time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC), + ), + shouldErr: true, + }, + { + name: "invalid time returns error", + report: types.NewReport( + 1, + 1, + []uint32{1}, + "", + types.NewPostTarget(1), + "cosmos1atdl3cpms89md5qa3rxtql0drtgftch2zgkr7v", + time.Time{}, + ), + shouldErr: true, + }, + { + name: "valid report returns no error", + report: types.NewReport( + 1, + 1, + []uint32{1}, + "", + types.NewPostTarget(1), + "cosmos1atdl3cpms89md5qa3rxtql0drtgftch2zgkr7v", + time.Date(2020, 1, 1, 12, 00, 00, 000, time.UTC), + ), + shouldErr: false, + }, + } + + for _, tc := range testCases { + tc := tc + t.Run(tc.name, func(t *testing.T) { + err := tc.report.Validate() + if tc.shouldErr { + require.Error(t, err) + } else { + require.NoError(t, err) + } + }) + } + +} + +// -------------------------------------------------------------------------------------------------------------------- + +func TestUserTarget_Validate(t *testing.T) { + testCases := []struct { + name string + data *types.UserTarget + shouldErr bool + }{ + { + name: "invalid user address returns error", + data: types.NewUserTarget(""), + shouldErr: true, + }, + { + name: "valid data returns no error", + data: types.NewUserTarget("cosmos1atdl3cpms89md5qa3rxtql0drtgftch2zgkr7v"), + shouldErr: false, + }, + } + + for _, tc := range testCases { + tc := tc + t.Run(tc.name, func(t *testing.T) { + err := tc.data.Validate() + if tc.shouldErr { + require.Error(t, err) + } else { + require.NoError(t, err) + } + }) + } +} + +// -------------------------------------------------------------------------------------------------------------------- + +func TestPostTarget_Validate(t *testing.T) { + testCases := []struct { + name string + data *types.PostTarget + shouldErr bool + }{ + { + name: "invalid post id returns error", + data: types.NewPostTarget(0), + shouldErr: true, + }, + { + name: "valid data returns no error", + data: types.NewPostTarget(1), + shouldErr: false, + }, + } + + for _, tc := range testCases { + tc := tc + t.Run(tc.name, func(t *testing.T) { + err := tc.data.Validate() + if tc.shouldErr { + require.Error(t, err) + } else { + require.NoError(t, err) + } + }) + } + +} + +// -------------------------------------------------------------------------------------------------------------------- + +func TestReason_Validate(t *testing.T) { + testCases := []struct { + name string + reason types.Reason + shouldErr bool + }{ + { + name: "invali subspace id returns error", + reason: types.NewReason(0, 1, "Spam", "This content is spam"), + shouldErr: true, + }, + { + name: "invalid id returns error", + reason: types.NewReason(1, 0, "Spam", "This content is spam"), + shouldErr: true, + }, + { + name: "invalid title returns error", + reason: types.NewReason(1, 1, "", "This content is spam"), + shouldErr: true, + }, + { + name: "valid reason returns no error", + reason: types.NewReason(1, 1, "Spam", "This content is spam"), + shouldErr: false, + }, + } + + for _, tc := range testCases { + tc := tc + t.Run(tc.name, func(t *testing.T) { + err := tc.reason.Validate() + if tc.shouldErr { + require.Error(t, err) + } else { + require.NoError(t, err) + } + }) + } + +} diff --git a/x/reports/types/msgs.go b/x/reports/types/msgs.go new file mode 100644 index 0000000000..31d2986809 --- /dev/null +++ b/x/reports/types/msgs.go @@ -0,0 +1,281 @@ +package types + +import ( + "fmt" + "strings" + + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +var ( + _ sdk.Msg = &MsgCreateReport{} + _ sdk.Msg = &MsgDeleteReport{} + _ sdk.Msg = &MsgSupportStandardReason{} + _ sdk.Msg = &MsgAddReason{} + _ sdk.Msg = &MsgRemoveReason{} +) + +// NewMsgCreateReport returns a new MsgCreateReport instance +func NewMsgCreateReport( + subspaceID uint64, + reasonsIDs []uint32, + message string, + target ReportTarget, + reporter string, +) *MsgCreateReport { + targetAny, err := codectypes.NewAnyWithValue(target) + if err != nil { + panic("failed to pack target to any type") + } + + return &MsgCreateReport{ + SubspaceID: subspaceID, + ReasonsIDs: reasonsIDs, + Message: message, + Reporter: reporter, + Target: targetAny, + } +} + +// UnpackInterfaces implements codectypes.UnpackInterfacesMessage +func (msg *MsgCreateReport) UnpackInterfaces(unpacker codectypes.AnyUnpacker) error { + var target ReportTarget + return unpacker.UnpackAny(msg.Target, &target) +} + +// Route should return the name of the module +func (msg MsgCreateReport) Route() string { return RouterKey } + +// Type should return the action +func (msg MsgCreateReport) Type() string { + return ActionCreateReport +} + +// ValidateBasic runs stateless checks on the message +func (msg MsgCreateReport) ValidateBasic() error { + if msg.SubspaceID == 0 { + return fmt.Errorf("invalid subspace id: %d", msg.SubspaceID) + } + + if len(msg.ReasonsIDs) == 0 { + return fmt.Errorf("at least one reporting reason is required") + } + + for _, reasonID := range msg.ReasonsIDs { + if reasonID == 0 { + return fmt.Errorf("invalid reason id: %d", reasonID) + } + } + + _, err := sdk.AccAddressFromBech32(msg.Reporter) + if err != nil { + return fmt.Errorf("invalid reporter address: %s", err) + } + + return msg.Target.GetCachedValue().(ReportTarget).Validate() +} + +// GetSignBytes encodes the message for signing +func (msg MsgCreateReport) GetSignBytes() []byte { + return sdk.MustSortJSON(AminoCdc.MustMarshalJSON(&msg)) +} + +// GetSigners defines whose signature is required +func (msg MsgCreateReport) GetSigners() []sdk.AccAddress { + sender, _ := sdk.AccAddressFromBech32(msg.Reporter) + return []sdk.AccAddress{sender} +} + +// -------------------------------------------------------------------------------------------------------------------- + +// NewMsgDeleteReport returns a new MsgDeleteReport instance +func NewMsgDeleteReport(subspaceID uint64, reportID uint64, signer string) *MsgDeleteReport { + return &MsgDeleteReport{ + SubspaceID: subspaceID, + ReportID: reportID, + Signer: signer, + } +} + +// Route should return the name of the module +func (msg MsgDeleteReport) Route() string { return RouterKey } + +// Type should return the action +func (msg MsgDeleteReport) Type() string { + return ActionDeleteReport +} + +// ValidateBasic runs stateless checks on the message +func (msg MsgDeleteReport) ValidateBasic() error { + if msg.SubspaceID == 0 { + return fmt.Errorf("invalid subspace id: %d", msg.SubspaceID) + } + + if msg.ReportID == 0 { + return fmt.Errorf("invalid report id: %d", msg.ReportID) + } + + _, err := sdk.AccAddressFromBech32(msg.Signer) + if err != nil { + return fmt.Errorf("invalid signer address: %s", err) + } + + return nil +} + +// GetSignBytes encodes the message for signing +func (msg MsgDeleteReport) GetSignBytes() []byte { + return sdk.MustSortJSON(AminoCdc.MustMarshalJSON(&msg)) +} + +// GetSigners defines whose signature is required +func (msg MsgDeleteReport) GetSigners() []sdk.AccAddress { + sender, _ := sdk.AccAddressFromBech32(msg.Signer) + return []sdk.AccAddress{sender} +} + +// -------------------------------------------------------------------------------------------------------------------- + +// NewMsgSupportStandardReason returns a new MsgSupportStandardReason instance +func NewMsgSupportStandardReason(subspaceID uint64, standardReasonID uint32, signer string) *MsgSupportStandardReason { + return &MsgSupportStandardReason{ + SubspaceID: subspaceID, + StandardReasonID: standardReasonID, + Signer: signer, + } +} + +// Route should return the name of the module +func (msg MsgSupportStandardReason) Route() string { return RouterKey } + +// Type should return the action +func (msg MsgSupportStandardReason) Type() string { + return ActionSupportStandardReason +} + +// ValidateBasic runs stateless checks on the message +func (msg MsgSupportStandardReason) ValidateBasic() error { + if msg.SubspaceID == 0 { + return fmt.Errorf("invalid subspace id: %d", msg.SubspaceID) + } + + if msg.StandardReasonID == 0 { + return fmt.Errorf("invalid standard reason id: %d", msg.StandardReasonID) + } + + _, err := sdk.AccAddressFromBech32(msg.Signer) + if err != nil { + return fmt.Errorf("invalid signer address: %s", err) + } + + return nil +} + +// GetSignBytes encodes the message for signing +func (msg MsgSupportStandardReason) GetSignBytes() []byte { + return sdk.MustSortJSON(AminoCdc.MustMarshalJSON(&msg)) +} + +// GetSigners defines whose signature is required +func (msg MsgSupportStandardReason) GetSigners() []sdk.AccAddress { + sender, _ := sdk.AccAddressFromBech32(msg.Signer) + return []sdk.AccAddress{sender} +} + +// -------------------------------------------------------------------------------------------------------------------- + +// NewMsgAddReason returns a new MsgAddReason instance +func NewMsgAddReason(subspaceID uint64, title string, description string, signer string) *MsgAddReason { + return &MsgAddReason{ + SubspaceID: subspaceID, + Title: title, + Description: description, + Signer: signer, + } +} + +// Route should return the name of the module +func (msg MsgAddReason) Route() string { return RouterKey } + +// Type should return the action +func (msg MsgAddReason) Type() string { + return ActionAddReason +} + +// ValidateBasic runs stateless checks on the message +func (msg MsgAddReason) ValidateBasic() error { + if msg.SubspaceID == 0 { + return fmt.Errorf("invalid subspace id: %d", msg.SubspaceID) + } + + if strings.TrimSpace(msg.Title) == "" { + return fmt.Errorf("invalid reason title: %s", msg.Title) + } + + _, err := sdk.AccAddressFromBech32(msg.Signer) + if err != nil { + return fmt.Errorf("invalid signer address: %s", err) + } + + return nil +} + +// GetSignBytes encodes the message for signing +func (msg MsgAddReason) GetSignBytes() []byte { + return sdk.MustSortJSON(AminoCdc.MustMarshalJSON(&msg)) +} + +// GetSigners defines whose signature is required +func (msg MsgAddReason) GetSigners() []sdk.AccAddress { + sender, _ := sdk.AccAddressFromBech32(msg.Signer) + return []sdk.AccAddress{sender} +} + +// -------------------------------------------------------------------------------------------------------------------- + +// NewMsgRemoveReason returns a new MsgRemoveReason instance +func NewMsgRemoveReason(subspaceID uint64, reasonID uint32, signer string) *MsgRemoveReason { + return &MsgRemoveReason{ + SubspaceID: subspaceID, + ReasonID: reasonID, + Signer: signer, + } +} + +// Route should return the name of the module +func (msg MsgRemoveReason) Route() string { return RouterKey } + +// Type should return the action +func (msg MsgRemoveReason) Type() string { + return ActionRemoveReason +} + +// ValidateBasic runs stateless checks on the message +func (msg MsgRemoveReason) ValidateBasic() error { + if msg.SubspaceID == 0 { + return fmt.Errorf("invalid subspace id: %d", msg.SubspaceID) + } + + if msg.ReasonID == 0 { + return fmt.Errorf("invalid reason id: %d", msg.ReasonID) + } + + _, err := sdk.AccAddressFromBech32(msg.Signer) + if err != nil { + return fmt.Errorf("invalid signer address: %s", err) + } + + return nil +} + +// GetSignBytes encodes the message for signing +func (msg MsgRemoveReason) GetSignBytes() []byte { + return sdk.MustSortJSON(AminoCdc.MustMarshalJSON(&msg)) +} + +// GetSigners defines whose signature is required +func (msg MsgRemoveReason) GetSigners() []sdk.AccAddress { + sender, _ := sdk.AccAddressFromBech32(msg.Signer) + return []sdk.AccAddress{sender} +} diff --git a/x/reports/types/msgs.pb.go b/x/reports/types/msgs.pb.go new file mode 100644 index 0000000000..f5e5384cfa --- /dev/null +++ b/x/reports/types/msgs.pb.go @@ -0,0 +1,2671 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: desmos/reports/v1/msgs.proto + +package types + +import ( + context "context" + fmt "fmt" + types "github.com/cosmos/cosmos-sdk/codec/types" + _ "github.com/gogo/protobuf/gogoproto" + grpc1 "github.com/gogo/protobuf/grpc" + proto "github.com/gogo/protobuf/proto" + github_com_gogo_protobuf_types "github.com/gogo/protobuf/types" + _ "github.com/regen-network/cosmos-proto" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" + _ "google.golang.org/protobuf/types/known/timestamppb" + io "io" + math "math" + math_bits "math/bits" + time "time" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf +var _ = time.Kitchen + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +// MsgCreateReport represents the message to be used to create a report +type MsgCreateReport struct { + // Id of the subspace for which the report should be stored + SubspaceID uint64 `protobuf:"varint,1,opt,name=subspace_id,json=subspaceId,proto3" json:"subspace_id,omitempty" yaml:"subspace_id"` + // Id of the reason this report has been created for + ReasonsIDs []uint32 `protobuf:"varint,2,rep,packed,name=reasons_ids,json=reasonsIds,proto3" json:"reasons_ids,omitempty" yaml:"reasons_ids"` + // (optional) Message attached to this report + Message string `protobuf:"bytes,3,opt,name=message,proto3" json:"message,omitempty" yaml:"message"` + // Address of the reporter + Reporter string `protobuf:"bytes,4,opt,name=reporter,proto3" json:"reporter,omitempty" yaml:"reporter"` + // Target of the report + Target *types.Any `protobuf:"bytes,5,opt,name=target,proto3" json:"target,omitempty" yaml:"target"` +} + +func (m *MsgCreateReport) Reset() { *m = MsgCreateReport{} } +func (m *MsgCreateReport) String() string { return proto.CompactTextString(m) } +func (*MsgCreateReport) ProtoMessage() {} +func (*MsgCreateReport) Descriptor() ([]byte, []int) { + return fileDescriptor_c7165cc8d939a535, []int{0} +} +func (m *MsgCreateReport) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgCreateReport) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgCreateReport.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgCreateReport) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgCreateReport.Merge(m, src) +} +func (m *MsgCreateReport) XXX_Size() int { + return m.Size() +} +func (m *MsgCreateReport) XXX_DiscardUnknown() { + xxx_messageInfo_MsgCreateReport.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgCreateReport proto.InternalMessageInfo + +func (m *MsgCreateReport) GetSubspaceID() uint64 { + if m != nil { + return m.SubspaceID + } + return 0 +} + +func (m *MsgCreateReport) GetReasonsIDs() []uint32 { + if m != nil { + return m.ReasonsIDs + } + return nil +} + +func (m *MsgCreateReport) GetMessage() string { + if m != nil { + return m.Message + } + return "" +} + +func (m *MsgCreateReport) GetReporter() string { + if m != nil { + return m.Reporter + } + return "" +} + +func (m *MsgCreateReport) GetTarget() *types.Any { + if m != nil { + return m.Target + } + return nil +} + +// MsgCreateReportResponse represents the Msg/CreateReport response type +type MsgCreateReportResponse struct { + // Id of the newly created report + ReportID uint64 `protobuf:"varint,1,opt,name=report_id,json=reportId,proto3" json:"report_id,omitempty" yaml:"report_id"` + // Time in which the report was created + CreationDate time.Time `protobuf:"bytes,2,opt,name=creation_date,json=creationDate,proto3,stdtime" json:"creation_date" yaml:"creation_date"` +} + +func (m *MsgCreateReportResponse) Reset() { *m = MsgCreateReportResponse{} } +func (m *MsgCreateReportResponse) String() string { return proto.CompactTextString(m) } +func (*MsgCreateReportResponse) ProtoMessage() {} +func (*MsgCreateReportResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_c7165cc8d939a535, []int{1} +} +func (m *MsgCreateReportResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgCreateReportResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgCreateReportResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgCreateReportResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgCreateReportResponse.Merge(m, src) +} +func (m *MsgCreateReportResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgCreateReportResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgCreateReportResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgCreateReportResponse proto.InternalMessageInfo + +func (m *MsgCreateReportResponse) GetReportID() uint64 { + if m != nil { + return m.ReportID + } + return 0 +} + +func (m *MsgCreateReportResponse) GetCreationDate() time.Time { + if m != nil { + return m.CreationDate + } + return time.Time{} +} + +// MsgDeleteReport represents the message to be used when deleting a report +type MsgDeleteReport struct { + // Id of the subspace that contains the report to be deleted + SubspaceID uint64 `protobuf:"varint,1,opt,name=subspace_id,json=subspaceId,proto3" json:"subspace_id,omitempty" yaml:"subspace_id"` + // Id of the report to be deleted + ReportID uint64 `protobuf:"varint,2,opt,name=report_id,json=reportId,proto3" json:"report_id,omitempty" yaml:"report_id"` + // Address of the user deleting the report + Signer string `protobuf:"bytes,3,opt,name=signer,proto3" json:"signer,omitempty" yaml:"signer"` +} + +func (m *MsgDeleteReport) Reset() { *m = MsgDeleteReport{} } +func (m *MsgDeleteReport) String() string { return proto.CompactTextString(m) } +func (*MsgDeleteReport) ProtoMessage() {} +func (*MsgDeleteReport) Descriptor() ([]byte, []int) { + return fileDescriptor_c7165cc8d939a535, []int{2} +} +func (m *MsgDeleteReport) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgDeleteReport) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgDeleteReport.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgDeleteReport) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgDeleteReport.Merge(m, src) +} +func (m *MsgDeleteReport) XXX_Size() int { + return m.Size() +} +func (m *MsgDeleteReport) XXX_DiscardUnknown() { + xxx_messageInfo_MsgDeleteReport.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgDeleteReport proto.InternalMessageInfo + +func (m *MsgDeleteReport) GetSubspaceID() uint64 { + if m != nil { + return m.SubspaceID + } + return 0 +} + +func (m *MsgDeleteReport) GetReportID() uint64 { + if m != nil { + return m.ReportID + } + return 0 +} + +func (m *MsgDeleteReport) GetSigner() string { + if m != nil { + return m.Signer + } + return "" +} + +// MsgDeleteReportResponse represents the Msg/DeleteReport response type +type MsgDeleteReportResponse struct { +} + +func (m *MsgDeleteReportResponse) Reset() { *m = MsgDeleteReportResponse{} } +func (m *MsgDeleteReportResponse) String() string { return proto.CompactTextString(m) } +func (*MsgDeleteReportResponse) ProtoMessage() {} +func (*MsgDeleteReportResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_c7165cc8d939a535, []int{3} +} +func (m *MsgDeleteReportResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgDeleteReportResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgDeleteReportResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgDeleteReportResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgDeleteReportResponse.Merge(m, src) +} +func (m *MsgDeleteReportResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgDeleteReportResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgDeleteReportResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgDeleteReportResponse proto.InternalMessageInfo + +// MsgSupportStandardReason represents the message to be used when wanting to +// support one reason from the module params +type MsgSupportStandardReason struct { + // Id of the subspace for which to support the reason + SubspaceID uint64 `protobuf:"varint,1,opt,name=subspace_id,json=subspaceId,proto3" json:"subspace_id,omitempty" yaml:"subspace_id"` + // Id of the reason that should be supported + StandardReasonID uint32 `protobuf:"varint,2,opt,name=standard_reason_id,json=standardReasonId,proto3" json:"standard_reason_id,omitempty" yaml:"standard_reason_id"` + // Address of the user signing the message + Signer string `protobuf:"bytes,3,opt,name=signer,proto3" json:"signer,omitempty" yaml:"signer"` +} + +func (m *MsgSupportStandardReason) Reset() { *m = MsgSupportStandardReason{} } +func (m *MsgSupportStandardReason) String() string { return proto.CompactTextString(m) } +func (*MsgSupportStandardReason) ProtoMessage() {} +func (*MsgSupportStandardReason) Descriptor() ([]byte, []int) { + return fileDescriptor_c7165cc8d939a535, []int{4} +} +func (m *MsgSupportStandardReason) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgSupportStandardReason) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgSupportStandardReason.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgSupportStandardReason) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgSupportStandardReason.Merge(m, src) +} +func (m *MsgSupportStandardReason) XXX_Size() int { + return m.Size() +} +func (m *MsgSupportStandardReason) XXX_DiscardUnknown() { + xxx_messageInfo_MsgSupportStandardReason.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgSupportStandardReason proto.InternalMessageInfo + +func (m *MsgSupportStandardReason) GetSubspaceID() uint64 { + if m != nil { + return m.SubspaceID + } + return 0 +} + +func (m *MsgSupportStandardReason) GetStandardReasonID() uint32 { + if m != nil { + return m.StandardReasonID + } + return 0 +} + +func (m *MsgSupportStandardReason) GetSigner() string { + if m != nil { + return m.Signer + } + return "" +} + +// MsgSupportStandardReasonResponse represents the Msg/SupportStandardReason +// response type +type MsgSupportStandardReasonResponse struct { + // Id of the newly added reason + ReasonsID uint32 `protobuf:"varint,1,opt,name=reasons_ids,json=reasonsIds,proto3" json:"reasons_ids,omitempty" yaml:"reasons_ids"` +} + +func (m *MsgSupportStandardReasonResponse) Reset() { *m = MsgSupportStandardReasonResponse{} } +func (m *MsgSupportStandardReasonResponse) String() string { return proto.CompactTextString(m) } +func (*MsgSupportStandardReasonResponse) ProtoMessage() {} +func (*MsgSupportStandardReasonResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_c7165cc8d939a535, []int{5} +} +func (m *MsgSupportStandardReasonResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgSupportStandardReasonResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgSupportStandardReasonResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgSupportStandardReasonResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgSupportStandardReasonResponse.Merge(m, src) +} +func (m *MsgSupportStandardReasonResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgSupportStandardReasonResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgSupportStandardReasonResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgSupportStandardReasonResponse proto.InternalMessageInfo + +func (m *MsgSupportStandardReasonResponse) GetReasonsID() uint32 { + if m != nil { + return m.ReasonsID + } + return 0 +} + +// MsgAddReason represents the message to be used when adding a new supported +// reason +type MsgAddReason struct { + // Id of the subspace for which to add the reason + SubspaceID uint64 `protobuf:"varint,1,opt,name=subspace_id,json=subspaceId,proto3" json:"subspace_id,omitempty" yaml:"subspace_id"` + // Title of the reason + Title string `protobuf:"bytes,2,opt,name=title,proto3" json:"title,omitempty" yaml:"title"` + // (optional) Extended description of the reason and the cases it applies to + Description string `protobuf:"bytes,3,opt,name=description,proto3" json:"description,omitempty" yaml:"description"` + // Address of the user adding the supported reason + Signer string `protobuf:"bytes,4,opt,name=signer,proto3" json:"signer,omitempty" yaml:"signer"` +} + +func (m *MsgAddReason) Reset() { *m = MsgAddReason{} } +func (m *MsgAddReason) String() string { return proto.CompactTextString(m) } +func (*MsgAddReason) ProtoMessage() {} +func (*MsgAddReason) Descriptor() ([]byte, []int) { + return fileDescriptor_c7165cc8d939a535, []int{6} +} +func (m *MsgAddReason) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgAddReason) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgAddReason.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgAddReason) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgAddReason.Merge(m, src) +} +func (m *MsgAddReason) XXX_Size() int { + return m.Size() +} +func (m *MsgAddReason) XXX_DiscardUnknown() { + xxx_messageInfo_MsgAddReason.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgAddReason proto.InternalMessageInfo + +func (m *MsgAddReason) GetSubspaceID() uint64 { + if m != nil { + return m.SubspaceID + } + return 0 +} + +func (m *MsgAddReason) GetTitle() string { + if m != nil { + return m.Title + } + return "" +} + +func (m *MsgAddReason) GetDescription() string { + if m != nil { + return m.Description + } + return "" +} + +func (m *MsgAddReason) GetSigner() string { + if m != nil { + return m.Signer + } + return "" +} + +// MsgAddReasonResponse represents the Msg/AddReason response type +type MsgAddReasonResponse struct { + // Id of the newly supported reason + ReasonID uint32 `protobuf:"varint,1,opt,name=reason_id,json=reasonId,proto3" json:"reason_id,omitempty" yaml:"reason_id"` +} + +func (m *MsgAddReasonResponse) Reset() { *m = MsgAddReasonResponse{} } +func (m *MsgAddReasonResponse) String() string { return proto.CompactTextString(m) } +func (*MsgAddReasonResponse) ProtoMessage() {} +func (*MsgAddReasonResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_c7165cc8d939a535, []int{7} +} +func (m *MsgAddReasonResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgAddReasonResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgAddReasonResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgAddReasonResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgAddReasonResponse.Merge(m, src) +} +func (m *MsgAddReasonResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgAddReasonResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgAddReasonResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgAddReasonResponse proto.InternalMessageInfo + +func (m *MsgAddReasonResponse) GetReasonID() uint32 { + if m != nil { + return m.ReasonID + } + return 0 +} + +// MsgRemoveReason represents the message to be used when removing an exiting +// reporting reason +type MsgRemoveReason struct { + // Id of the subspace from which to remove the reason + SubspaceID uint64 `protobuf:"varint,1,opt,name=subspace_id,json=subspaceId,proto3" json:"subspace_id,omitempty" yaml:"subspace_id"` + // Id of the reason to be deleted + ReasonID uint32 `protobuf:"varint,2,opt,name=reason_id,json=reasonId,proto3" json:"reason_id,omitempty" yaml:"reason_id"` + // Address of the user adding the supported reason + Signer string `protobuf:"bytes,3,opt,name=signer,proto3" json:"signer,omitempty" yaml:"signer"` +} + +func (m *MsgRemoveReason) Reset() { *m = MsgRemoveReason{} } +func (m *MsgRemoveReason) String() string { return proto.CompactTextString(m) } +func (*MsgRemoveReason) ProtoMessage() {} +func (*MsgRemoveReason) Descriptor() ([]byte, []int) { + return fileDescriptor_c7165cc8d939a535, []int{8} +} +func (m *MsgRemoveReason) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgRemoveReason) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgRemoveReason.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgRemoveReason) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgRemoveReason.Merge(m, src) +} +func (m *MsgRemoveReason) XXX_Size() int { + return m.Size() +} +func (m *MsgRemoveReason) XXX_DiscardUnknown() { + xxx_messageInfo_MsgRemoveReason.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgRemoveReason proto.InternalMessageInfo + +func (m *MsgRemoveReason) GetSubspaceID() uint64 { + if m != nil { + return m.SubspaceID + } + return 0 +} + +func (m *MsgRemoveReason) GetReasonID() uint32 { + if m != nil { + return m.ReasonID + } + return 0 +} + +func (m *MsgRemoveReason) GetSigner() string { + if m != nil { + return m.Signer + } + return "" +} + +// MsgRemoveReasonResponse represents the Msg/RemoveReason response type +type MsgRemoveReasonResponse struct { +} + +func (m *MsgRemoveReasonResponse) Reset() { *m = MsgRemoveReasonResponse{} } +func (m *MsgRemoveReasonResponse) String() string { return proto.CompactTextString(m) } +func (*MsgRemoveReasonResponse) ProtoMessage() {} +func (*MsgRemoveReasonResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_c7165cc8d939a535, []int{9} +} +func (m *MsgRemoveReasonResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgRemoveReasonResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgRemoveReasonResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgRemoveReasonResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgRemoveReasonResponse.Merge(m, src) +} +func (m *MsgRemoveReasonResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgRemoveReasonResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgRemoveReasonResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgRemoveReasonResponse proto.InternalMessageInfo + +func init() { + proto.RegisterType((*MsgCreateReport)(nil), "desmos.reports.v1.MsgCreateReport") + proto.RegisterType((*MsgCreateReportResponse)(nil), "desmos.reports.v1.MsgCreateReportResponse") + proto.RegisterType((*MsgDeleteReport)(nil), "desmos.reports.v1.MsgDeleteReport") + proto.RegisterType((*MsgDeleteReportResponse)(nil), "desmos.reports.v1.MsgDeleteReportResponse") + proto.RegisterType((*MsgSupportStandardReason)(nil), "desmos.reports.v1.MsgSupportStandardReason") + proto.RegisterType((*MsgSupportStandardReasonResponse)(nil), "desmos.reports.v1.MsgSupportStandardReasonResponse") + proto.RegisterType((*MsgAddReason)(nil), "desmos.reports.v1.MsgAddReason") + proto.RegisterType((*MsgAddReasonResponse)(nil), "desmos.reports.v1.MsgAddReasonResponse") + proto.RegisterType((*MsgRemoveReason)(nil), "desmos.reports.v1.MsgRemoveReason") + proto.RegisterType((*MsgRemoveReasonResponse)(nil), "desmos.reports.v1.MsgRemoveReasonResponse") +} + +func init() { proto.RegisterFile("desmos/reports/v1/msgs.proto", fileDescriptor_c7165cc8d939a535) } + +var fileDescriptor_c7165cc8d939a535 = []byte{ + // 793 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x56, 0x4f, 0x6f, 0xda, 0x4e, + 0x10, 0xc5, 0x90, 0xe4, 0x17, 0x36, 0xf0, 0x0b, 0x71, 0x69, 0x4b, 0x50, 0x85, 0xd1, 0xb6, 0x6a, + 0xe9, 0x3f, 0xbb, 0x49, 0x2e, 0x55, 0xa5, 0xaa, 0x0a, 0xa5, 0x07, 0x2a, 0x71, 0xd9, 0x34, 0x97, + 0x1e, 0x82, 0x0c, 0xde, 0x3a, 0x96, 0x30, 0x6b, 0x79, 0x0d, 0x2a, 0xdf, 0x22, 0xdf, 0xa7, 0x87, + 0x4a, 0x3d, 0xe5, 0x98, 0x63, 0x4f, 0x6e, 0xe5, 0x9c, 0x7b, 0xe1, 0xd2, 0x6b, 0x65, 0xef, 0xda, + 0xac, 0x13, 0x10, 0x42, 0xe2, 0xc6, 0xce, 0xbc, 0x7d, 0x33, 0xf3, 0x66, 0x66, 0x0d, 0x78, 0x60, + 0x60, 0x6a, 0x13, 0xaa, 0xb9, 0xd8, 0x21, 0xae, 0x47, 0xb5, 0xf1, 0x81, 0x66, 0x53, 0x93, 0xaa, + 0x8e, 0x4b, 0x3c, 0x22, 0xef, 0x31, 0xaf, 0xca, 0xbd, 0xea, 0xf8, 0xa0, 0x5a, 0x36, 0x89, 0x49, + 0x22, 0xaf, 0x16, 0xfe, 0x62, 0xc0, 0xea, 0xbe, 0x49, 0x88, 0x39, 0xc0, 0x5a, 0x74, 0xea, 0x8d, + 0xbe, 0x68, 0xfa, 0x70, 0xc2, 0x5d, 0xca, 0x4d, 0x97, 0x67, 0xd9, 0x98, 0x7a, 0xba, 0xed, 0xc4, + 0x77, 0xfb, 0x24, 0x0c, 0xd2, 0x65, 0xa4, 0xec, 0xc0, 0x5d, 0xb5, 0x39, 0xd9, 0x11, 0x03, 0x0f, + 0xb8, 0x1f, 0x7e, 0xcf, 0x82, 0xdd, 0x0e, 0x35, 0xdf, 0xbb, 0x58, 0xf7, 0x30, 0x8a, 0x40, 0xf2, + 0x07, 0xb0, 0x43, 0x47, 0x3d, 0xea, 0xe8, 0x7d, 0xdc, 0xb5, 0x8c, 0x8a, 0x54, 0x97, 0x1a, 0x1b, + 0xcd, 0x47, 0x81, 0xaf, 0x80, 0x13, 0x6e, 0x6e, 0xb7, 0xa6, 0xbe, 0x22, 0x4f, 0x74, 0x7b, 0xf0, + 0x06, 0x0a, 0x50, 0x88, 0x40, 0x7c, 0x6a, 0x1b, 0x21, 0x8d, 0x8b, 0x75, 0x4a, 0x86, 0xb4, 0x6b, + 0x19, 0xb4, 0x92, 0xad, 0xe7, 0x1a, 0x45, 0x46, 0x83, 0x98, 0xb9, 0xdd, 0xa2, 0x33, 0x1a, 0x01, + 0x0a, 0x11, 0xe0, 0xa7, 0xb6, 0x41, 0xe5, 0x17, 0xe0, 0x3f, 0x1b, 0x53, 0xaa, 0x9b, 0xb8, 0x92, + 0xab, 0x4b, 0x8d, 0x7c, 0x53, 0x9e, 0xfa, 0xca, 0xff, 0xec, 0x12, 0x77, 0x40, 0x14, 0x43, 0x64, + 0x0d, 0x6c, 0xb3, 0x52, 0xb1, 0x5b, 0xd9, 0x88, 0xe0, 0x77, 0xa6, 0xbe, 0xb2, 0x1b, 0xc7, 0x60, + 0x1e, 0x88, 0x12, 0x90, 0xfc, 0x0e, 0x6c, 0x79, 0xba, 0x6b, 0x62, 0xaf, 0xb2, 0x59, 0x97, 0x1a, + 0x3b, 0x87, 0x65, 0x95, 0xa9, 0xad, 0xc6, 0x6a, 0xab, 0xc7, 0xc3, 0x49, 0x73, 0x6f, 0xea, 0x2b, + 0x45, 0x46, 0xc2, 0xd0, 0x10, 0xf1, 0x6b, 0xf0, 0x9b, 0x04, 0xee, 0xdf, 0x50, 0x10, 0x61, 0xea, + 0x90, 0x21, 0xc5, 0xf2, 0x5b, 0x90, 0x67, 0x81, 0x66, 0x3a, 0xd6, 0x03, 0x5f, 0xd9, 0x66, 0xb0, + 0x48, 0xc5, 0x92, 0x98, 0x5a, 0xa4, 0x21, 0xcf, 0xad, 0x6d, 0xc8, 0x3a, 0x28, 0xf6, 0x43, 0x5a, + 0x8b, 0x0c, 0xbb, 0x86, 0xee, 0xe1, 0x4a, 0x36, 0x4a, 0xb1, 0x7a, 0x2b, 0xc5, 0x4f, 0xf1, 0x40, + 0x34, 0xeb, 0x97, 0xbe, 0x92, 0x99, 0xfa, 0x4a, 0x99, 0xd1, 0xa6, 0xae, 0xc3, 0x8b, 0x5f, 0x8a, + 0x84, 0x0a, 0xb1, 0xad, 0x15, 0x9a, 0x7e, 0x48, 0x51, 0xff, 0x5b, 0x78, 0x80, 0xd7, 0xdd, 0xff, + 0x54, 0xf1, 0xd9, 0x95, 0x8b, 0x7f, 0x0a, 0xb6, 0xa8, 0x65, 0x0e, 0xb1, 0xcb, 0xdb, 0x2e, 0xb4, + 0x80, 0xd9, 0x21, 0xe2, 0x00, 0xb8, 0x1f, 0x75, 0x40, 0xac, 0x21, 0xee, 0x00, 0xfc, 0x23, 0x81, + 0x4a, 0x87, 0x9a, 0x27, 0x23, 0x27, 0x34, 0x9f, 0x78, 0xfa, 0xd0, 0xd0, 0x5d, 0x83, 0x0d, 0xe0, + 0xba, 0x0a, 0xed, 0x02, 0x99, 0x72, 0xe2, 0x2e, 0x1b, 0xdc, 0xb8, 0xe2, 0x62, 0xf3, 0x20, 0xf0, + 0x95, 0x52, 0x3a, 0x6c, 0xc4, 0xb9, 0xcf, 0x39, 0x6f, 0xdd, 0x83, 0xa8, 0x44, 0xd3, 0xf0, 0x95, + 0xa4, 0x38, 0x07, 0xf5, 0x45, 0xe5, 0x26, 0x53, 0xd9, 0x4a, 0x2f, 0xa6, 0x14, 0x25, 0xfa, 0x30, + 0xf0, 0x95, 0x7c, 0xb2, 0x98, 0xcb, 0xf7, 0x12, 0x06, 0x12, 0x28, 0x74, 0xa8, 0x79, 0x6c, 0xac, + 0x59, 0xcd, 0xc7, 0x60, 0xd3, 0xb3, 0xbc, 0x01, 0x1b, 0xf6, 0x7c, 0xb3, 0x34, 0xf5, 0x95, 0x02, + 0xdf, 0xbc, 0xd0, 0x0c, 0x11, 0x73, 0xcb, 0xaf, 0xc1, 0x8e, 0x81, 0x69, 0xdf, 0xb5, 0x9c, 0x70, + 0x98, 0xb9, 0x32, 0xf7, 0x66, 0x01, 0x04, 0x27, 0x44, 0x22, 0x54, 0x90, 0x73, 0x63, 0x99, 0x9c, + 0xa7, 0xa0, 0x2c, 0xd6, 0x98, 0x5e, 0xec, 0xb8, 0xd3, 0x4c, 0x40, 0x3e, 0xdb, 0x49, 0x87, 0x4b, + 0xa2, 0x7e, 0xf1, 0x6c, 0xb3, 0x86, 0xc6, 0x5b, 0x87, 0xb0, 0x4d, 0xc6, 0x78, 0xbd, 0xf2, 0xa5, + 0x32, 0xcb, 0xae, 0x9a, 0xd9, 0xea, 0x5b, 0x27, 0xd6, 0x10, 0xcb, 0x73, 0xf8, 0x37, 0x07, 0x72, + 0x1d, 0x6a, 0xca, 0x67, 0xa0, 0x90, 0xfa, 0xb2, 0x40, 0xf5, 0xd6, 0xe7, 0x50, 0xbd, 0xf1, 0x76, + 0x56, 0x9f, 0x2d, 0xc7, 0x24, 0x6d, 0x38, 0x03, 0x85, 0xd4, 0xcb, 0xb5, 0x80, 0x5f, 0xc4, 0x2c, + 0xe2, 0x9f, 0xf7, 0x7a, 0xc8, 0x13, 0x70, 0x77, 0xfe, 0xcb, 0xf1, 0x7c, 0x3e, 0xc9, 0x5c, 0x70, + 0xf5, 0x68, 0x05, 0x70, 0x12, 0xfa, 0x14, 0xe4, 0x67, 0xab, 0xa5, 0xcc, 0x67, 0x48, 0x00, 0xd5, + 0x27, 0x4b, 0x00, 0xa2, 0x62, 0xa9, 0xa9, 0x5b, 0xa0, 0x98, 0x88, 0x59, 0xa4, 0xd8, 0xbc, 0xce, + 0x37, 0x3f, 0x5e, 0x06, 0x35, 0xe9, 0x2a, 0xa8, 0x49, 0xbf, 0x83, 0x9a, 0x74, 0x71, 0x5d, 0xcb, + 0x5c, 0x5d, 0xd7, 0x32, 0x3f, 0xaf, 0x6b, 0x99, 0xcf, 0xaf, 0x4c, 0xcb, 0x3b, 0x1f, 0xf5, 0xd4, + 0x3e, 0xb1, 0x35, 0xc6, 0xf7, 0x72, 0xa0, 0xf7, 0x28, 0xff, 0xad, 0x8d, 0x8f, 0xb4, 0xaf, 0xc9, + 0xbf, 0x14, 0x6f, 0xe2, 0x60, 0xda, 0xdb, 0x8a, 0xbe, 0x6f, 0x47, 0xff, 0x02, 0x00, 0x00, 0xff, + 0xff, 0xc3, 0x96, 0x8f, 0x4d, 0x62, 0x09, 0x00, 0x00, +} + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ grpc.ClientConn + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +const _ = grpc.SupportPackageIsVersion4 + +// MsgClient is the client API for Msg service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. +type MsgClient interface { + // CreateReport allows to create a new report + CreateReport(ctx context.Context, in *MsgCreateReport, opts ...grpc.CallOption) (*MsgCreateReportResponse, error) + // DeleteReport allows to delete an existing report + DeleteReport(ctx context.Context, in *MsgDeleteReport, opts ...grpc.CallOption) (*MsgDeleteReportResponse, error) + // SupportStandardReason allows to support one of the reasons present inside + // the module params + SupportStandardReason(ctx context.Context, in *MsgSupportStandardReason, opts ...grpc.CallOption) (*MsgSupportStandardReasonResponse, error) + // AddReason allows to add a new supported reporting reason + AddReason(ctx context.Context, in *MsgAddReason, opts ...grpc.CallOption) (*MsgAddReasonResponse, error) + // RemoveReason allows to remove a supported reporting reason + RemoveReason(ctx context.Context, in *MsgRemoveReason, opts ...grpc.CallOption) (*MsgRemoveReasonResponse, error) +} + +type msgClient struct { + cc grpc1.ClientConn +} + +func NewMsgClient(cc grpc1.ClientConn) MsgClient { + return &msgClient{cc} +} + +func (c *msgClient) CreateReport(ctx context.Context, in *MsgCreateReport, opts ...grpc.CallOption) (*MsgCreateReportResponse, error) { + out := new(MsgCreateReportResponse) + err := c.cc.Invoke(ctx, "/desmos.reports.v1.Msg/CreateReport", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *msgClient) DeleteReport(ctx context.Context, in *MsgDeleteReport, opts ...grpc.CallOption) (*MsgDeleteReportResponse, error) { + out := new(MsgDeleteReportResponse) + err := c.cc.Invoke(ctx, "/desmos.reports.v1.Msg/DeleteReport", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *msgClient) SupportStandardReason(ctx context.Context, in *MsgSupportStandardReason, opts ...grpc.CallOption) (*MsgSupportStandardReasonResponse, error) { + out := new(MsgSupportStandardReasonResponse) + err := c.cc.Invoke(ctx, "/desmos.reports.v1.Msg/SupportStandardReason", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *msgClient) AddReason(ctx context.Context, in *MsgAddReason, opts ...grpc.CallOption) (*MsgAddReasonResponse, error) { + out := new(MsgAddReasonResponse) + err := c.cc.Invoke(ctx, "/desmos.reports.v1.Msg/AddReason", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *msgClient) RemoveReason(ctx context.Context, in *MsgRemoveReason, opts ...grpc.CallOption) (*MsgRemoveReasonResponse, error) { + out := new(MsgRemoveReasonResponse) + err := c.cc.Invoke(ctx, "/desmos.reports.v1.Msg/RemoveReason", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// MsgServer is the server API for Msg service. +type MsgServer interface { + // CreateReport allows to create a new report + CreateReport(context.Context, *MsgCreateReport) (*MsgCreateReportResponse, error) + // DeleteReport allows to delete an existing report + DeleteReport(context.Context, *MsgDeleteReport) (*MsgDeleteReportResponse, error) + // SupportStandardReason allows to support one of the reasons present inside + // the module params + SupportStandardReason(context.Context, *MsgSupportStandardReason) (*MsgSupportStandardReasonResponse, error) + // AddReason allows to add a new supported reporting reason + AddReason(context.Context, *MsgAddReason) (*MsgAddReasonResponse, error) + // RemoveReason allows to remove a supported reporting reason + RemoveReason(context.Context, *MsgRemoveReason) (*MsgRemoveReasonResponse, error) +} + +// UnimplementedMsgServer can be embedded to have forward compatible implementations. +type UnimplementedMsgServer struct { +} + +func (*UnimplementedMsgServer) CreateReport(ctx context.Context, req *MsgCreateReport) (*MsgCreateReportResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method CreateReport not implemented") +} +func (*UnimplementedMsgServer) DeleteReport(ctx context.Context, req *MsgDeleteReport) (*MsgDeleteReportResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method DeleteReport not implemented") +} +func (*UnimplementedMsgServer) SupportStandardReason(ctx context.Context, req *MsgSupportStandardReason) (*MsgSupportStandardReasonResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method SupportStandardReason not implemented") +} +func (*UnimplementedMsgServer) AddReason(ctx context.Context, req *MsgAddReason) (*MsgAddReasonResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method AddReason not implemented") +} +func (*UnimplementedMsgServer) RemoveReason(ctx context.Context, req *MsgRemoveReason) (*MsgRemoveReasonResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method RemoveReason not implemented") +} + +func RegisterMsgServer(s grpc1.Server, srv MsgServer) { + s.RegisterService(&_Msg_serviceDesc, srv) +} + +func _Msg_CreateReport_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgCreateReport) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).CreateReport(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/desmos.reports.v1.Msg/CreateReport", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).CreateReport(ctx, req.(*MsgCreateReport)) + } + return interceptor(ctx, in, info, handler) +} + +func _Msg_DeleteReport_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgDeleteReport) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).DeleteReport(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/desmos.reports.v1.Msg/DeleteReport", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).DeleteReport(ctx, req.(*MsgDeleteReport)) + } + return interceptor(ctx, in, info, handler) +} + +func _Msg_SupportStandardReason_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgSupportStandardReason) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).SupportStandardReason(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/desmos.reports.v1.Msg/SupportStandardReason", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).SupportStandardReason(ctx, req.(*MsgSupportStandardReason)) + } + return interceptor(ctx, in, info, handler) +} + +func _Msg_AddReason_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgAddReason) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).AddReason(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/desmos.reports.v1.Msg/AddReason", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).AddReason(ctx, req.(*MsgAddReason)) + } + return interceptor(ctx, in, info, handler) +} + +func _Msg_RemoveReason_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgRemoveReason) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).RemoveReason(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/desmos.reports.v1.Msg/RemoveReason", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).RemoveReason(ctx, req.(*MsgRemoveReason)) + } + return interceptor(ctx, in, info, handler) +} + +var _Msg_serviceDesc = grpc.ServiceDesc{ + ServiceName: "desmos.reports.v1.Msg", + HandlerType: (*MsgServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "CreateReport", + Handler: _Msg_CreateReport_Handler, + }, + { + MethodName: "DeleteReport", + Handler: _Msg_DeleteReport_Handler, + }, + { + MethodName: "SupportStandardReason", + Handler: _Msg_SupportStandardReason_Handler, + }, + { + MethodName: "AddReason", + Handler: _Msg_AddReason_Handler, + }, + { + MethodName: "RemoveReason", + Handler: _Msg_RemoveReason_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "desmos/reports/v1/msgs.proto", +} + +func (m *MsgCreateReport) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgCreateReport) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgCreateReport) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Target != nil { + { + size, err := m.Target.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintMsgs(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2a + } + if len(m.Reporter) > 0 { + i -= len(m.Reporter) + copy(dAtA[i:], m.Reporter) + i = encodeVarintMsgs(dAtA, i, uint64(len(m.Reporter))) + i-- + dAtA[i] = 0x22 + } + if len(m.Message) > 0 { + i -= len(m.Message) + copy(dAtA[i:], m.Message) + i = encodeVarintMsgs(dAtA, i, uint64(len(m.Message))) + i-- + dAtA[i] = 0x1a + } + if len(m.ReasonsIDs) > 0 { + dAtA3 := make([]byte, len(m.ReasonsIDs)*10) + var j2 int + for _, num := range m.ReasonsIDs { + for num >= 1<<7 { + dAtA3[j2] = uint8(uint64(num)&0x7f | 0x80) + num >>= 7 + j2++ + } + dAtA3[j2] = uint8(num) + j2++ + } + i -= j2 + copy(dAtA[i:], dAtA3[:j2]) + i = encodeVarintMsgs(dAtA, i, uint64(j2)) + i-- + dAtA[i] = 0x12 + } + if m.SubspaceID != 0 { + i = encodeVarintMsgs(dAtA, i, uint64(m.SubspaceID)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *MsgCreateReportResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgCreateReportResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgCreateReportResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + n4, err4 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.CreationDate, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.CreationDate):]) + if err4 != nil { + return 0, err4 + } + i -= n4 + i = encodeVarintMsgs(dAtA, i, uint64(n4)) + i-- + dAtA[i] = 0x12 + if m.ReportID != 0 { + i = encodeVarintMsgs(dAtA, i, uint64(m.ReportID)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *MsgDeleteReport) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgDeleteReport) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgDeleteReport) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Signer) > 0 { + i -= len(m.Signer) + copy(dAtA[i:], m.Signer) + i = encodeVarintMsgs(dAtA, i, uint64(len(m.Signer))) + i-- + dAtA[i] = 0x1a + } + if m.ReportID != 0 { + i = encodeVarintMsgs(dAtA, i, uint64(m.ReportID)) + i-- + dAtA[i] = 0x10 + } + if m.SubspaceID != 0 { + i = encodeVarintMsgs(dAtA, i, uint64(m.SubspaceID)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *MsgDeleteReportResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgDeleteReportResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgDeleteReportResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *MsgSupportStandardReason) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgSupportStandardReason) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgSupportStandardReason) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Signer) > 0 { + i -= len(m.Signer) + copy(dAtA[i:], m.Signer) + i = encodeVarintMsgs(dAtA, i, uint64(len(m.Signer))) + i-- + dAtA[i] = 0x1a + } + if m.StandardReasonID != 0 { + i = encodeVarintMsgs(dAtA, i, uint64(m.StandardReasonID)) + i-- + dAtA[i] = 0x10 + } + if m.SubspaceID != 0 { + i = encodeVarintMsgs(dAtA, i, uint64(m.SubspaceID)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *MsgSupportStandardReasonResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgSupportStandardReasonResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgSupportStandardReasonResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.ReasonsID != 0 { + i = encodeVarintMsgs(dAtA, i, uint64(m.ReasonsID)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *MsgAddReason) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgAddReason) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgAddReason) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Signer) > 0 { + i -= len(m.Signer) + copy(dAtA[i:], m.Signer) + i = encodeVarintMsgs(dAtA, i, uint64(len(m.Signer))) + i-- + dAtA[i] = 0x22 + } + if len(m.Description) > 0 { + i -= len(m.Description) + copy(dAtA[i:], m.Description) + i = encodeVarintMsgs(dAtA, i, uint64(len(m.Description))) + i-- + dAtA[i] = 0x1a + } + if len(m.Title) > 0 { + i -= len(m.Title) + copy(dAtA[i:], m.Title) + i = encodeVarintMsgs(dAtA, i, uint64(len(m.Title))) + i-- + dAtA[i] = 0x12 + } + if m.SubspaceID != 0 { + i = encodeVarintMsgs(dAtA, i, uint64(m.SubspaceID)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *MsgAddReasonResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgAddReasonResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgAddReasonResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.ReasonID != 0 { + i = encodeVarintMsgs(dAtA, i, uint64(m.ReasonID)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *MsgRemoveReason) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgRemoveReason) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgRemoveReason) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Signer) > 0 { + i -= len(m.Signer) + copy(dAtA[i:], m.Signer) + i = encodeVarintMsgs(dAtA, i, uint64(len(m.Signer))) + i-- + dAtA[i] = 0x1a + } + if m.ReasonID != 0 { + i = encodeVarintMsgs(dAtA, i, uint64(m.ReasonID)) + i-- + dAtA[i] = 0x10 + } + if m.SubspaceID != 0 { + i = encodeVarintMsgs(dAtA, i, uint64(m.SubspaceID)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *MsgRemoveReasonResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgRemoveReasonResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgRemoveReasonResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func encodeVarintMsgs(dAtA []byte, offset int, v uint64) int { + offset -= sovMsgs(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *MsgCreateReport) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.SubspaceID != 0 { + n += 1 + sovMsgs(uint64(m.SubspaceID)) + } + if len(m.ReasonsIDs) > 0 { + l = 0 + for _, e := range m.ReasonsIDs { + l += sovMsgs(uint64(e)) + } + n += 1 + sovMsgs(uint64(l)) + l + } + l = len(m.Message) + if l > 0 { + n += 1 + l + sovMsgs(uint64(l)) + } + l = len(m.Reporter) + if l > 0 { + n += 1 + l + sovMsgs(uint64(l)) + } + if m.Target != nil { + l = m.Target.Size() + n += 1 + l + sovMsgs(uint64(l)) + } + return n +} + +func (m *MsgCreateReportResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.ReportID != 0 { + n += 1 + sovMsgs(uint64(m.ReportID)) + } + l = github_com_gogo_protobuf_types.SizeOfStdTime(m.CreationDate) + n += 1 + l + sovMsgs(uint64(l)) + return n +} + +func (m *MsgDeleteReport) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.SubspaceID != 0 { + n += 1 + sovMsgs(uint64(m.SubspaceID)) + } + if m.ReportID != 0 { + n += 1 + sovMsgs(uint64(m.ReportID)) + } + l = len(m.Signer) + if l > 0 { + n += 1 + l + sovMsgs(uint64(l)) + } + return n +} + +func (m *MsgDeleteReportResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *MsgSupportStandardReason) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.SubspaceID != 0 { + n += 1 + sovMsgs(uint64(m.SubspaceID)) + } + if m.StandardReasonID != 0 { + n += 1 + sovMsgs(uint64(m.StandardReasonID)) + } + l = len(m.Signer) + if l > 0 { + n += 1 + l + sovMsgs(uint64(l)) + } + return n +} + +func (m *MsgSupportStandardReasonResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.ReasonsID != 0 { + n += 1 + sovMsgs(uint64(m.ReasonsID)) + } + return n +} + +func (m *MsgAddReason) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.SubspaceID != 0 { + n += 1 + sovMsgs(uint64(m.SubspaceID)) + } + l = len(m.Title) + if l > 0 { + n += 1 + l + sovMsgs(uint64(l)) + } + l = len(m.Description) + if l > 0 { + n += 1 + l + sovMsgs(uint64(l)) + } + l = len(m.Signer) + if l > 0 { + n += 1 + l + sovMsgs(uint64(l)) + } + return n +} + +func (m *MsgAddReasonResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.ReasonID != 0 { + n += 1 + sovMsgs(uint64(m.ReasonID)) + } + return n +} + +func (m *MsgRemoveReason) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.SubspaceID != 0 { + n += 1 + sovMsgs(uint64(m.SubspaceID)) + } + if m.ReasonID != 0 { + n += 1 + sovMsgs(uint64(m.ReasonID)) + } + l = len(m.Signer) + if l > 0 { + n += 1 + l + sovMsgs(uint64(l)) + } + return n +} + +func (m *MsgRemoveReasonResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func sovMsgs(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozMsgs(x uint64) (n int) { + return sovMsgs(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *MsgCreateReport) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMsgs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgCreateReport: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgCreateReport: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field SubspaceID", wireType) + } + m.SubspaceID = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMsgs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.SubspaceID |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType == 0 { + var v uint32 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMsgs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.ReasonsIDs = append(m.ReasonsIDs, v) + } else if wireType == 2 { + var packedLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMsgs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + packedLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if packedLen < 0 { + return ErrInvalidLengthMsgs + } + postIndex := iNdEx + packedLen + if postIndex < 0 { + return ErrInvalidLengthMsgs + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var elementCount int + var count int + for _, integer := range dAtA[iNdEx:postIndex] { + if integer < 128 { + count++ + } + } + elementCount = count + if elementCount != 0 && len(m.ReasonsIDs) == 0 { + m.ReasonsIDs = make([]uint32, 0, elementCount) + } + for iNdEx < postIndex { + var v uint32 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMsgs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.ReasonsIDs = append(m.ReasonsIDs, v) + } + } else { + return fmt.Errorf("proto: wrong wireType = %d for field ReasonsIDs", wireType) + } + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Message", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMsgs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthMsgs + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthMsgs + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Message = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Reporter", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMsgs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthMsgs + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthMsgs + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Reporter = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Target", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMsgs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthMsgs + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthMsgs + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Target == nil { + m.Target = &types.Any{} + } + if err := m.Target.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipMsgs(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthMsgs + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgCreateReportResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMsgs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgCreateReportResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgCreateReportResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ReportID", wireType) + } + m.ReportID = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMsgs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.ReportID |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CreationDate", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMsgs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthMsgs + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthMsgs + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := github_com_gogo_protobuf_types.StdTimeUnmarshal(&m.CreationDate, dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipMsgs(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthMsgs + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgDeleteReport) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMsgs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgDeleteReport: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgDeleteReport: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field SubspaceID", wireType) + } + m.SubspaceID = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMsgs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.SubspaceID |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ReportID", wireType) + } + m.ReportID = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMsgs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.ReportID |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Signer", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMsgs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthMsgs + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthMsgs + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Signer = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipMsgs(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthMsgs + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgDeleteReportResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMsgs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgDeleteReportResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgDeleteReportResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipMsgs(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthMsgs + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgSupportStandardReason) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMsgs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgSupportStandardReason: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgSupportStandardReason: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field SubspaceID", wireType) + } + m.SubspaceID = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMsgs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.SubspaceID |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field StandardReasonID", wireType) + } + m.StandardReasonID = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMsgs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.StandardReasonID |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Signer", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMsgs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthMsgs + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthMsgs + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Signer = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipMsgs(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthMsgs + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgSupportStandardReasonResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMsgs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgSupportStandardReasonResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgSupportStandardReasonResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ReasonsID", wireType) + } + m.ReasonsID = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMsgs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.ReasonsID |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipMsgs(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthMsgs + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgAddReason) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMsgs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgAddReason: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgAddReason: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field SubspaceID", wireType) + } + m.SubspaceID = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMsgs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.SubspaceID |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Title", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMsgs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthMsgs + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthMsgs + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Title = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Description", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMsgs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthMsgs + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthMsgs + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Description = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Signer", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMsgs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthMsgs + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthMsgs + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Signer = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipMsgs(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthMsgs + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgAddReasonResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMsgs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgAddReasonResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgAddReasonResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ReasonID", wireType) + } + m.ReasonID = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMsgs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.ReasonID |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipMsgs(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthMsgs + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgRemoveReason) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMsgs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgRemoveReason: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgRemoveReason: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field SubspaceID", wireType) + } + m.SubspaceID = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMsgs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.SubspaceID |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ReasonID", wireType) + } + m.ReasonID = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMsgs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.ReasonID |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Signer", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMsgs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthMsgs + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthMsgs + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Signer = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipMsgs(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthMsgs + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgRemoveReasonResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMsgs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgRemoveReasonResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgRemoveReasonResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipMsgs(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthMsgs + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipMsgs(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowMsgs + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowMsgs + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowMsgs + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthMsgs + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupMsgs + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthMsgs + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthMsgs = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowMsgs = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupMsgs = fmt.Errorf("proto: unexpected end of group") +) diff --git a/x/reports/types/msgs_test.go b/x/reports/types/msgs_test.go new file mode 100644 index 0000000000..724525f873 --- /dev/null +++ b/x/reports/types/msgs_test.go @@ -0,0 +1,437 @@ +package types_test + +import ( + "testing" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/require" + + "github.com/desmos-labs/desmos/v3/x/reports/types" +) + +var msgCreateReport = types.NewMsgCreateReport( + 1, + []uint32{1}, + "This post is spam", + types.NewPostTarget(1), + "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47", +) + +func TestMsgCreateReport_Route(t *testing.T) { + require.Equal(t, types.ModuleName, msgCreateReport.Route()) +} + +func TestMsgCreateReport_Type(t *testing.T) { + require.Equal(t, types.ActionCreateReport, msgCreateReport.Type()) +} + +func TestMsgCreateReport_ValidateBasic(t *testing.T) { + testCases := []struct { + name string + msg *types.MsgCreateReport + shouldErr bool + }{ + { + name: "invalid subspace id returns error", + msg: types.NewMsgCreateReport( + 0, + []uint32{1}, + "This post is spam", + types.NewPostTarget(1), + "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47", + ), + shouldErr: true, + }, + { + name: "empty reasons returns error", + msg: types.NewMsgCreateReport( + 1, + nil, + "This post is spam", + types.NewPostTarget(1), + "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47", + ), + shouldErr: true, + }, + { + name: "invalid reason id returns error", + msg: types.NewMsgCreateReport( + 1, + []uint32{0}, + "This post is spam", + types.NewPostTarget(1), + "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47", + ), + shouldErr: true, + }, + { + name: "invalid reporter returns error", + msg: types.NewMsgCreateReport( + 1, + []uint32{1}, + "This post is spam", + types.NewPostTarget(1), + "", + ), + shouldErr: true, + }, + { + name: "invalid report target returns error", + msg: types.NewMsgCreateReport( + 1, + []uint32{1}, + "This post is spam", + types.NewPostTarget(0), + "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47", + ), + shouldErr: true, + }, + { + name: "valid message returns no error", + msg: msgCreateReport, + shouldErr: false, + }, + } + + for _, tc := range testCases { + tc := tc + t.Run(tc.name, func(t *testing.T) { + err := tc.msg.ValidateBasic() + if tc.shouldErr { + require.Error(t, err) + } else { + require.NoError(t, err) + } + }) + } +} + +func TestMsgCreateReport_GetSignBytes(t *testing.T) { + expected := `{"type":"desmos/MsgCreateReport","value":{"message":"This post is spam","reasons_ids":[1],"reporter":"cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47","subspace_id":"1","target":{"type":"desmos/PostTarget","value":{"post_id":"1"}}}}` + require.Equal(t, expected, string(msgCreateReport.GetSignBytes())) +} + +func TestMsgCreateReport_GetSigners(t *testing.T) { + addr, _ := sdk.AccAddressFromBech32(msgCreateReport.Reporter) + require.Equal(t, []sdk.AccAddress{addr}, msgCreateReport.GetSigners()) +} + +// -------------------------------------------------------------------------------------------------------------------- + +var msgDeleteReport = types.NewMsgDeleteReport( + 1, + 1, + "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47", +) + +func TestMsgDeleteReport_Route(t *testing.T) { + require.Equal(t, types.ModuleName, msgDeleteReport.Route()) +} + +func TestMsgDeleteReport_Type(t *testing.T) { + require.Equal(t, types.ActionDeleteReport, msgDeleteReport.Type()) +} + +func TestMsgDeleteReport_ValidateBasic(t *testing.T) { + testCases := []struct { + name string + msg *types.MsgDeleteReport + shouldErr bool + }{ + { + name: "invalid subspace id returns error", + msg: types.NewMsgDeleteReport( + 0, + 1, + "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47", + ), + shouldErr: true, + }, + { + name: "invalid report id returns error", + msg: types.NewMsgDeleteReport( + 1, + 0, + "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47", + ), + shouldErr: true, + }, + { + name: "invalid signer returns error", + msg: types.NewMsgDeleteReport( + 1, + 1, + "", + ), + shouldErr: true, + }, + { + name: "valid message returns no error", + msg: msgDeleteReport, + shouldErr: false, + }, + } + + for _, tc := range testCases { + tc := tc + t.Run(tc.name, func(t *testing.T) { + err := tc.msg.ValidateBasic() + if tc.shouldErr { + require.Error(t, err) + } else { + require.NoError(t, err) + } + }) + } +} + +func TestMsgDeleteReport_GetSignBytes(t *testing.T) { + expected := `{"type":"desmos/MsgDeleteReport","value":{"report_id":"1","signer":"cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47","subspace_id":"1"}}` + require.Equal(t, expected, string(msgDeleteReport.GetSignBytes())) +} + +func TestMsgDeleteReport_GetSigners(t *testing.T) { + addr, _ := sdk.AccAddressFromBech32(msgDeleteReport.Signer) + require.Equal(t, []sdk.AccAddress{addr}, msgDeleteReport.GetSigners()) +} + +// -------------------------------------------------------------------------------------------------------------------- + +var msgSupportStandardReason = types.NewMsgSupportStandardReason( + 1, + 1, + "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47", +) + +func TestMsgSupportStandardReason_Route(t *testing.T) { + require.Equal(t, types.ModuleName, msgSupportStandardReason.Route()) +} + +func TestMsgSupportStandardReason_Type(t *testing.T) { + require.Equal(t, types.ActionSupportStandardReason, msgSupportStandardReason.Type()) +} + +func TestMsgSupportStandardReason_ValidateBasic(t *testing.T) { + testCases := []struct { + name string + msg *types.MsgSupportStandardReason + shouldErr bool + }{ + { + name: "invalid subspace id returns error", + msg: types.NewMsgSupportStandardReason( + 0, + 1, + "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47", + ), + shouldErr: true, + }, + { + name: "invalid reason id returns error", + msg: types.NewMsgSupportStandardReason( + 1, + 0, + "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47", + ), + shouldErr: true, + }, + { + name: "invalid reporter returns error", + msg: types.NewMsgSupportStandardReason( + 1, + 1, + "", + ), + shouldErr: true, + }, + { + name: "valid message returns no error", + msg: msgSupportStandardReason, + shouldErr: false, + }, + } + + for _, tc := range testCases { + tc := tc + t.Run(tc.name, func(t *testing.T) { + err := tc.msg.ValidateBasic() + if tc.shouldErr { + require.Error(t, err) + } else { + require.NoError(t, err) + } + }) + } +} + +func TestMsgSupportStandardReason_GetSignBytes(t *testing.T) { + expected := `{"type":"desmos/MsgSupportStandardReason","value":{"signer":"cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47","standard_reason_id":1,"subspace_id":"1"}}` + require.Equal(t, expected, string(msgSupportStandardReason.GetSignBytes())) +} + +func TestMsgSupportStandardReason_GetSigners(t *testing.T) { + addr, _ := sdk.AccAddressFromBech32(msgSupportStandardReason.Signer) + require.Equal(t, []sdk.AccAddress{addr}, msgSupportStandardReason.GetSigners()) +} + +// -------------------------------------------------------------------------------------------------------------------- + +var msgAddReason = types.NewMsgAddReason( + 1, + "Spam", + "This post is spam or the user is a spammer", + "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47", +) + +func TestMsgAddReason_Route(t *testing.T) { + require.Equal(t, types.ModuleName, msgAddReason.Route()) +} + +func TestMsgAddReason_Type(t *testing.T) { + require.Equal(t, types.ActionAddReason, msgAddReason.Type()) +} + +func TestMsgAddReason_ValidateBasic(t *testing.T) { + testCases := []struct { + name string + msg *types.MsgAddReason + shouldErr bool + }{ + { + name: "invalid subspace id returns error", + msg: types.NewMsgAddReason( + 0, + "Spam", + "This post is spam or the user is a spammer", + "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47", + ), + shouldErr: true, + }, + { + name: "invalid title returns error", + msg: types.NewMsgAddReason( + 1, + "", + "This post is spam or the user is a spammer", + "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47", + ), + shouldErr: true, + }, + { + name: "invalid signer returns error", + msg: types.NewMsgAddReason( + 1, + "Spam", + "This post is spam or the user is a spammer", + "", + ), + shouldErr: true, + }, + { + name: "valid message returns no error", + msg: msgAddReason, + shouldErr: false, + }, + } + + for _, tc := range testCases { + tc := tc + t.Run(tc.name, func(t *testing.T) { + err := tc.msg.ValidateBasic() + if tc.shouldErr { + require.Error(t, err) + } else { + require.NoError(t, err) + } + }) + } +} + +func TestMsgAddReason_GetSignBytes(t *testing.T) { + expected := `{"type":"desmos/MsgAddReason","value":{"description":"This post is spam or the user is a spammer","signer":"cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47","subspace_id":"1","title":"Spam"}}` + require.Equal(t, expected, string(msgAddReason.GetSignBytes())) +} + +func TestMsgAddReason_GetSigners(t *testing.T) { + addr, _ := sdk.AccAddressFromBech32(msgAddReason.Signer) + require.Equal(t, []sdk.AccAddress{addr}, msgAddReason.GetSigners()) +} + +// -------------------------------------------------------------------------------------------------------------------- + +var msgRemoveReason = types.NewMsgRemoveReason( + 1, + 1, + "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47", +) + +func TestMsgRemoveReason_Route(t *testing.T) { + require.Equal(t, types.ModuleName, msgRemoveReason.Route()) +} + +func TestMsgRemoveReason_Type(t *testing.T) { + require.Equal(t, types.ActionRemoveReason, msgRemoveReason.Type()) +} + +func TestMsgRemoveReason_ValidateBasic(t *testing.T) { + testCases := []struct { + name string + msg *types.MsgRemoveReason + shouldErr bool + }{ + { + name: "invalid subspace id returns error", + msg: types.NewMsgRemoveReason( + 0, + 1, + "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47", + ), + shouldErr: true, + }, + { + name: "invalid reason id returns error", + msg: types.NewMsgRemoveReason( + 1, + 0, + "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47", + ), + shouldErr: true, + }, + { + name: "invalid signer returns error", + msg: types.NewMsgRemoveReason( + 1, + 1, + "", + ), + shouldErr: true, + }, + { + name: "valid message returns no error", + msg: msgRemoveReason, + shouldErr: false, + }, + } + + for _, tc := range testCases { + tc := tc + t.Run(tc.name, func(t *testing.T) { + err := tc.msg.ValidateBasic() + if tc.shouldErr { + require.Error(t, err) + } else { + require.NoError(t, err) + } + }) + } +} + +func TestMsgRemoveReason_GetSignBytes(t *testing.T) { + expected := `{"type":"desmos/MsgRemoveReason","value":{"reason_id":1,"signer":"cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47","subspace_id":"1"}}` + require.Equal(t, expected, string(msgRemoveReason.GetSignBytes())) +} + +func TestMsgRemoveReason_GetSigners(t *testing.T) { + addr, _ := sdk.AccAddressFromBech32(msgRemoveReason.Signer) + require.Equal(t, []sdk.AccAddress{addr}, msgRemoveReason.GetSigners()) +} diff --git a/x/reports/types/params.go b/x/reports/types/params.go new file mode 100644 index 0000000000..591f5e659b --- /dev/null +++ b/x/reports/types/params.go @@ -0,0 +1,122 @@ +package types + +import ( + "fmt" + "strings" + + paramstypes "github.com/cosmos/cosmos-sdk/x/params/types" +) + +const ( + // DefaultParamsSpace represents the default paramspace for the Params keeper + DefaultParamsSpace = ModuleName +) + +var ( + // DefaultReasons represents the default set of reasons that can be adopted by subspaces + DefaultReasons []StandardReason +) + +var ( + // ReasonsKey represents the params key used to store available default reasons + ReasonsKey = []byte("StandardReasons") +) + +// -------------------------------------------------------------------------------------------------------------------- + +// ParamKeyTable returns the key declaration for the parameters +func ParamKeyTable() paramstypes.KeyTable { + return paramstypes.NewKeyTable(). + RegisterParamSet(&Params{}) +} + +// NewParams returns a new Params instance +func NewParams(reasons []StandardReason) Params { + return Params{ + StandardReasons: reasons, + } +} + +// DefaultParams returns the default params +func DefaultParams() Params { + return Params{ + StandardReasons: DefaultReasons, + } +} + +// ParamSetPairs implements the ParamSet interface and returns the key/value pairs +// of reports module's parameters. +func (params *Params) ParamSetPairs() paramstypes.ParamSetPairs { + return paramstypes.ParamSetPairs{ + paramstypes.NewParamSetPair(ReasonsKey, ¶ms.StandardReasons, ValidateStandardReasonsParam), + } +} + +// Validate perform basic checks on all parameters to ensure they are correct +func (params Params) Validate() error { + return ValidateStandardReasonsParam(params.StandardReasons) +} + +// ValidateStandardReasonsParam validates the reasons params value +func ValidateStandardReasonsParam(i interface{}) error { + reasons, ok := i.([]StandardReason) + if !ok { + return fmt.Errorf("invalid parameters type: %s", i) + } + + err := NewStandardReasons(reasons...).Validate() + if err != nil { + return err + } + + return nil +} + +// -------------------------------------------------------------------------------------------------------------------- + +type StandardReasons []StandardReason + +// NewStandardReasons returns a new instance of StandardReasons contains the given reasons +func NewStandardReasons(reasons ...StandardReason) StandardReasons { + return reasons +} + +// Validate implements fmt.Validator +func (s StandardReasons) Validate() error { + ids := map[uint32]bool{} + for _, reason := range s { + if _, duplicated := ids[reason.ID]; duplicated { + return fmt.Errorf("duplicated reason with id %d", reason.ID) + } + ids[reason.ID] = true + + err := reason.Validate() + if err != nil { + return err + } + } + + return nil +} + +// NewStandardReason returns a new StandardReason instance +func NewStandardReason(id uint32, title string, description string) StandardReason { + return StandardReason{ + ID: id, + Title: title, + Description: description, + } +} + +// Validate implements fmt.Validator +func (r StandardReason) Validate() error { + if r.ID == 0 { + return fmt.Errorf("invalid id: %d", r.ID) + } + + if strings.TrimSpace(r.Title) == "" { + return fmt.Errorf("invalid title: %s", r.Title) + } + + return nil +} diff --git a/x/reports/types/params_test.go b/x/reports/types/params_test.go new file mode 100644 index 0000000000..2a649bb9b5 --- /dev/null +++ b/x/reports/types/params_test.go @@ -0,0 +1,102 @@ +package types_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/desmos-labs/desmos/v3/x/reports/types" +) + +func TestParams_Validate(t *testing.T) { + testCases := []struct { + name string + params types.Params + shouldErr bool + }{ + { + name: "invalid reasons return error", + params: types.NewParams( + types.NewStandardReasons( + types.NewStandardReason(1, "Spam", "This content is spam"), + types.NewStandardReason(1, "Harm", "This content contains self-harm/suicide images"), + ), + ), + shouldErr: true, + }, + { + name: "valid params return no error", + params: types.NewParams( + types.NewStandardReasons( + types.NewStandardReason(1, "Spam", "This content is spam"), + types.NewStandardReason(2, "Harm", "This content contains self-harm/suicide images"), + ), + ), + shouldErr: false, + }, + { + name: "default params return no error", + params: types.DefaultParams(), + shouldErr: false, + }, + } + + for _, tc := range testCases { + tc := tc + t.Run(tc.name, func(t *testing.T) { + err := tc.params.Validate() + if tc.shouldErr { + require.Error(t, err) + } else { + require.NoError(t, err) + } + }) + } + +} + +func TestStandardReasons_Validate(t *testing.T) { + testCases := []struct { + name string + reasons types.StandardReasons + shouldErr bool + }{ + { + name: "duplicated id returns error", + reasons: types.NewStandardReasons( + types.NewStandardReason(1, "Spam", "This content is spam"), + types.NewStandardReason(1, "Harm", "This content contains self-harm/suicide images"), + ), + shouldErr: true, + }, + { + name: "invalid reason returns error", + reasons: types.NewStandardReasons( + types.NewStandardReason(1, "Spam", "This content is spam"), + types.NewStandardReason(2, "", "This content contains self-harm/suicide images"), + ), + shouldErr: true, + }, + { + name: "valid reasons return no error", + reasons: types.NewStandardReasons( + types.NewStandardReason(1, "Spam", "This content is spam"), + types.NewStandardReason(2, "Harm", "This content contains self-harm/suicide images"), + ), + shouldErr: false, + }, + } + + for _, tc := range testCases { + tc := tc + t.Run(tc.name, func(t *testing.T) { + err := tc.reasons.Validate() + if tc.shouldErr { + require.Error(t, err) + } else { + require.NoError(t, err) + } + }) + } + +} diff --git a/x/reports/types/query.go b/x/reports/types/query.go new file mode 100644 index 0000000000..3b5a900042 --- /dev/null +++ b/x/reports/types/query.go @@ -0,0 +1,55 @@ +package types + +import ( + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/cosmos/cosmos-sdk/types/query" +) + +// UnpackInterfaces implements codectypes.UnpackInterfacesMessage +func (r *QueryReportsRequest) UnpackInterfaces(unpacker codectypes.AnyUnpacker) error { + var target ReportTarget + return unpacker.UnpackAny(r.Target, &target) +} + +// NewQueryReportsRequest returns a new QueryReportsRequest instance +func NewQueryReportsRequest(subspaceID uint64, target ReportTarget, reporter string, pagination *query.PageRequest) *QueryReportsRequest { + var targetAny *codectypes.Any + if target != nil { + any, err := codectypes.NewAnyWithValue(target) + if err != nil { + panic("failed to pack target to any type") + } + targetAny = any + } + + return &QueryReportsRequest{ + SubspaceId: subspaceID, + Target: targetAny, + Reporter: reporter, + Pagination: pagination, + } +} + +// UnpackInterfaces implements codectypes.UnpackInterfacesMessage +func (r *QueryReportsResponse) UnpackInterfaces(unpacker codectypes.AnyUnpacker) error { + for _, report := range r.Reports { + err := report.UnpackInterfaces(unpacker) + if err != nil { + return err + } + } + return nil +} + +// NewQueryReasonsRequest returns a new QueryReasonsRequest instance +func NewQueryReasonsRequest(subspaceID uint64, pagination *query.PageRequest) *QueryReasonsRequest { + return &QueryReasonsRequest{ + SubspaceId: subspaceID, + Pagination: pagination, + } +} + +// NewQueryParamsRequest returns a new QueryParamsRequest instance +func NewQueryParamsRequest() *QueryParamsRequest { + return &QueryParamsRequest{} +} diff --git a/x/reports/types/query.pb.go b/x/reports/types/query.pb.go new file mode 100644 index 0000000000..cb3f443bae --- /dev/null +++ b/x/reports/types/query.pb.go @@ -0,0 +1,1667 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: desmos/reports/v1/query.proto + +package types + +import ( + context "context" + fmt "fmt" + types "github.com/cosmos/cosmos-sdk/codec/types" + query "github.com/cosmos/cosmos-sdk/types/query" + _ "github.com/gogo/protobuf/gogoproto" + grpc1 "github.com/gogo/protobuf/grpc" + proto "github.com/gogo/protobuf/proto" + _ "github.com/regen-network/cosmos-proto" + _ "google.golang.org/genproto/googleapis/api/annotations" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +// QueryReportsResponse is the request type for Query/Reports RPC method +type QueryReportsRequest struct { + // Id of the subspace to query the reports for + SubspaceId uint64 `protobuf:"varint,1,opt,name=subspace_id,json=subspaceId,proto3" json:"subspace_id,omitempty" yaml:"subspace_id"` + // (optional) Target to query the reports for + Target *types.Any `protobuf:"bytes,2,opt,name=target,proto3" json:"target,omitempty" yaml:"target"` + // (optional) User that reported the target. + // This is going to be used only if the target is also specified + Reporter string `protobuf:"bytes,3,opt,name=reporter,proto3" json:"reporter,omitempty" yaml:"reporter"` + // pagination defines an optional pagination for the request. + Pagination *query.PageRequest `protobuf:"bytes,4,opt,name=pagination,proto3" json:"pagination,omitempty" yaml:"pagination"` +} + +func (m *QueryReportsRequest) Reset() { *m = QueryReportsRequest{} } +func (m *QueryReportsRequest) String() string { return proto.CompactTextString(m) } +func (*QueryReportsRequest) ProtoMessage() {} +func (*QueryReportsRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_f0e9c7b1802c3e20, []int{0} +} +func (m *QueryReportsRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryReportsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryReportsRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *QueryReportsRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryReportsRequest.Merge(m, src) +} +func (m *QueryReportsRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryReportsRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryReportsRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryReportsRequest proto.InternalMessageInfo + +func (m *QueryReportsRequest) GetSubspaceId() uint64 { + if m != nil { + return m.SubspaceId + } + return 0 +} + +func (m *QueryReportsRequest) GetTarget() *types.Any { + if m != nil { + return m.Target + } + return nil +} + +func (m *QueryReportsRequest) GetReporter() string { + if m != nil { + return m.Reporter + } + return "" +} + +func (m *QueryReportsRequest) GetPagination() *query.PageRequest { + if m != nil { + return m.Pagination + } + return nil +} + +// QueryReportsResponse is the response type for Query/Reports RPC method +type QueryReportsResponse struct { + Reports []Report `protobuf:"bytes,1,rep,name=reports,proto3" json:"reports" yaml:"reports"` + Pagination *query.PageResponse `protobuf:"bytes,2,opt,name=pagination,proto3" json:"pagination,omitempty" yaml:"pagination"` +} + +func (m *QueryReportsResponse) Reset() { *m = QueryReportsResponse{} } +func (m *QueryReportsResponse) String() string { return proto.CompactTextString(m) } +func (*QueryReportsResponse) ProtoMessage() {} +func (*QueryReportsResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_f0e9c7b1802c3e20, []int{1} +} +func (m *QueryReportsResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryReportsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryReportsResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *QueryReportsResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryReportsResponse.Merge(m, src) +} +func (m *QueryReportsResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryReportsResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryReportsResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryReportsResponse proto.InternalMessageInfo + +func (m *QueryReportsResponse) GetReports() []Report { + if m != nil { + return m.Reports + } + return nil +} + +func (m *QueryReportsResponse) GetPagination() *query.PageResponse { + if m != nil { + return m.Pagination + } + return nil +} + +// QueryReasonsRequest is the request type for Query/Reasons RPC method +type QueryReasonsRequest struct { + // Id of the subspace to query the supported reporting reasons for + SubspaceId uint64 `protobuf:"varint,1,opt,name=subspace_id,json=subspaceId,proto3" json:"subspace_id,omitempty" yaml:"subspace_id"` + // pagination defines an optional pagination for the request. + Pagination *query.PageRequest `protobuf:"bytes,3,opt,name=pagination,proto3" json:"pagination,omitempty" yaml:"pagination"` +} + +func (m *QueryReasonsRequest) Reset() { *m = QueryReasonsRequest{} } +func (m *QueryReasonsRequest) String() string { return proto.CompactTextString(m) } +func (*QueryReasonsRequest) ProtoMessage() {} +func (*QueryReasonsRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_f0e9c7b1802c3e20, []int{2} +} +func (m *QueryReasonsRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryReasonsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryReasonsRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *QueryReasonsRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryReasonsRequest.Merge(m, src) +} +func (m *QueryReasonsRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryReasonsRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryReasonsRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryReasonsRequest proto.InternalMessageInfo + +func (m *QueryReasonsRequest) GetSubspaceId() uint64 { + if m != nil { + return m.SubspaceId + } + return 0 +} + +func (m *QueryReasonsRequest) GetPagination() *query.PageRequest { + if m != nil { + return m.Pagination + } + return nil +} + +// QueryReasonsResponse is the response type for Query/Reasons RPC method +type QueryReasonsResponse struct { + Reasons []Reason `protobuf:"bytes,1,rep,name=reasons,proto3" json:"reasons" yaml:"reasons"` + Pagination *query.PageResponse `protobuf:"bytes,2,opt,name=pagination,proto3" json:"pagination,omitempty" yaml:"pagination"` +} + +func (m *QueryReasonsResponse) Reset() { *m = QueryReasonsResponse{} } +func (m *QueryReasonsResponse) String() string { return proto.CompactTextString(m) } +func (*QueryReasonsResponse) ProtoMessage() {} +func (*QueryReasonsResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_f0e9c7b1802c3e20, []int{3} +} +func (m *QueryReasonsResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryReasonsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryReasonsResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *QueryReasonsResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryReasonsResponse.Merge(m, src) +} +func (m *QueryReasonsResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryReasonsResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryReasonsResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryReasonsResponse proto.InternalMessageInfo + +func (m *QueryReasonsResponse) GetReasons() []Reason { + if m != nil { + return m.Reasons + } + return nil +} + +func (m *QueryReasonsResponse) GetPagination() *query.PageResponse { + if m != nil { + return m.Pagination + } + return nil +} + +// QueryParamsRequest is the request type for Query/Params RPC method +type QueryParamsRequest struct { +} + +func (m *QueryParamsRequest) Reset() { *m = QueryParamsRequest{} } +func (m *QueryParamsRequest) String() string { return proto.CompactTextString(m) } +func (*QueryParamsRequest) ProtoMessage() {} +func (*QueryParamsRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_f0e9c7b1802c3e20, []int{4} +} +func (m *QueryParamsRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryParamsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryParamsRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *QueryParamsRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryParamsRequest.Merge(m, src) +} +func (m *QueryParamsRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryParamsRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryParamsRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryParamsRequest proto.InternalMessageInfo + +// QueryParamsResponse is the response type for Query/Params RPC method +type QueryParamsResponse struct { + Params Params `protobuf:"bytes,1,opt,name=params,proto3" json:"params" yaml:"params"` +} + +func (m *QueryParamsResponse) Reset() { *m = QueryParamsResponse{} } +func (m *QueryParamsResponse) String() string { return proto.CompactTextString(m) } +func (*QueryParamsResponse) ProtoMessage() {} +func (*QueryParamsResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_f0e9c7b1802c3e20, []int{5} +} +func (m *QueryParamsResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryParamsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryParamsResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *QueryParamsResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryParamsResponse.Merge(m, src) +} +func (m *QueryParamsResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryParamsResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryParamsResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryParamsResponse proto.InternalMessageInfo + +func (m *QueryParamsResponse) GetParams() Params { + if m != nil { + return m.Params + } + return Params{} +} + +func init() { + proto.RegisterType((*QueryReportsRequest)(nil), "desmos.reports.v1.QueryReportsRequest") + proto.RegisterType((*QueryReportsResponse)(nil), "desmos.reports.v1.QueryReportsResponse") + proto.RegisterType((*QueryReasonsRequest)(nil), "desmos.reports.v1.QueryReasonsRequest") + proto.RegisterType((*QueryReasonsResponse)(nil), "desmos.reports.v1.QueryReasonsResponse") + proto.RegisterType((*QueryParamsRequest)(nil), "desmos.reports.v1.QueryParamsRequest") + proto.RegisterType((*QueryParamsResponse)(nil), "desmos.reports.v1.QueryParamsResponse") +} + +func init() { proto.RegisterFile("desmos/reports/v1/query.proto", fileDescriptor_f0e9c7b1802c3e20) } + +var fileDescriptor_f0e9c7b1802c3e20 = []byte{ + // 635 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x54, 0xcf, 0x6e, 0x12, 0x41, + 0x1c, 0x66, 0x4b, 0xa5, 0x3a, 0xa4, 0x1a, 0xa6, 0xb4, 0x01, 0xd4, 0x5d, 0xdc, 0x44, 0x4a, 0x9a, + 0x38, 0x53, 0xe8, 0xc1, 0xc4, 0x8b, 0x71, 0x4f, 0xfe, 0xb9, 0xd4, 0x3d, 0x9a, 0x28, 0x99, 0x85, + 0x71, 0x25, 0x81, 0x9d, 0xed, 0xce, 0x42, 0x44, 0xe3, 0xc5, 0xb3, 0x07, 0x13, 0x1f, 0xc0, 0xf8, + 0x00, 0x3e, 0x85, 0x97, 0x1e, 0x9b, 0x78, 0xf1, 0xb4, 0x31, 0xe0, 0x13, 0xf0, 0x04, 0x66, 0x67, + 0x66, 0xe9, 0x12, 0x10, 0x34, 0xa6, 0x37, 0x66, 0xbe, 0xdf, 0xef, 0xfb, 0x7d, 0xdf, 0xf7, 0x1b, + 0x16, 0xdc, 0xec, 0x50, 0xde, 0x67, 0x1c, 0x07, 0xd4, 0x67, 0x41, 0xc8, 0xf1, 0xb0, 0x81, 0x4f, + 0x06, 0x34, 0x18, 0x21, 0x3f, 0x60, 0x21, 0x83, 0x05, 0x09, 0x23, 0x05, 0xa3, 0x61, 0xa3, 0x52, + 0x74, 0x99, 0xcb, 0x04, 0x8a, 0xe3, 0x5f, 0xb2, 0xb0, 0x72, 0xc3, 0x65, 0xcc, 0xed, 0x51, 0x4c, + 0xfc, 0x2e, 0x26, 0x9e, 0xc7, 0x42, 0x12, 0x76, 0x99, 0xc7, 0x15, 0x5a, 0x56, 0xa8, 0x38, 0x39, + 0x83, 0x97, 0x98, 0x78, 0xa3, 0x04, 0x6a, 0xb3, 0x78, 0x42, 0x4b, 0x32, 0xca, 0x83, 0x82, 0x0e, + 0xe4, 0x09, 0x3b, 0x84, 0x53, 0xa9, 0x0a, 0x0f, 0x1b, 0x0e, 0x0d, 0x49, 0x03, 0xfb, 0xc4, 0xed, + 0x7a, 0x62, 0x84, 0xaa, 0xd5, 0x17, 0x7d, 0xf4, 0x59, 0x87, 0xf6, 0x14, 0x97, 0xf9, 0x79, 0x03, + 0xec, 0x3c, 0x8d, 0x29, 0x6c, 0x59, 0x60, 0xd3, 0x93, 0x01, 0xe5, 0x21, 0xbc, 0x0b, 0xf2, 0x7c, + 0xe0, 0x70, 0x9f, 0xb4, 0x69, 0xab, 0xdb, 0x29, 0x69, 0x55, 0xad, 0xbe, 0x69, 0xed, 0x4d, 0x23, + 0x03, 0x8e, 0x48, 0xbf, 0x77, 0xcf, 0x4c, 0x81, 0xa6, 0x0d, 0x92, 0xd3, 0xa3, 0x0e, 0xbc, 0x0f, + 0x72, 0x21, 0x09, 0x5c, 0x1a, 0x96, 0x36, 0xaa, 0x5a, 0x3d, 0xdf, 0x2c, 0x22, 0xe9, 0x11, 0x25, + 0x1e, 0xd1, 0x03, 0x6f, 0x64, 0x15, 0xa6, 0x91, 0xb1, 0x2d, 0x99, 0x64, 0xb5, 0x69, 0xab, 0x36, + 0x88, 0xc1, 0x65, 0x29, 0x96, 0x06, 0xa5, 0x6c, 0x55, 0xab, 0x5f, 0xb1, 0x76, 0xa6, 0x91, 0x71, + 0x4d, 0x16, 0x27, 0x88, 0x69, 0xcf, 0x8a, 0xe0, 0x73, 0x00, 0xce, 0x6d, 0x97, 0x36, 0xc5, 0xd4, + 0x1a, 0x52, 0x89, 0xc5, 0x19, 0x21, 0xb9, 0x39, 0x95, 0x11, 0x3a, 0x26, 0x2e, 0x55, 0x36, 0xad, + 0xdd, 0x69, 0x64, 0x14, 0x24, 0xf5, 0x39, 0x87, 0x69, 0xa7, 0x08, 0xcd, 0x6f, 0x1a, 0x28, 0xce, + 0x27, 0xc4, 0x7d, 0xe6, 0x71, 0x0a, 0x9f, 0x80, 0x2d, 0x95, 0x6a, 0x49, 0xab, 0x66, 0xeb, 0xf9, + 0x66, 0x19, 0x2d, 0xbc, 0x0a, 0x24, 0x9b, 0xac, 0xbd, 0xd3, 0xc8, 0xc8, 0x4c, 0x23, 0xe3, 0x6a, + 0xda, 0x06, 0x37, 0xed, 0x84, 0x01, 0xbe, 0x98, 0x33, 0x21, 0xa3, 0xdb, 0x5f, 0x6b, 0x42, 0x2a, + 0xf9, 0x1b, 0x17, 0x5f, 0xb5, 0xd9, 0x9e, 0x09, 0x67, 0xde, 0xff, 0xef, 0x79, 0x3e, 0xf5, 0xec, + 0x05, 0xa6, 0xae, 0xf4, 0xa6, 0x53, 0x17, 0x57, 0x2b, 0x53, 0x8f, 0x2b, 0x16, 0x53, 0x17, 0x7d, + 0x22, 0x75, 0xf1, 0xeb, 0xc2, 0x53, 0x2f, 0x02, 0x28, 0x4c, 0x1c, 0x93, 0x80, 0xf4, 0x93, 0xcc, + 0xcd, 0x96, 0x5a, 0x45, 0x72, 0xab, 0x9c, 0x3d, 0x04, 0x39, 0x5f, 0xdc, 0x88, 0x2d, 0x2c, 0x37, + 0x26, 0x5b, 0xac, 0x5d, 0x65, 0x6c, 0x3b, 0x19, 0x1f, 0xdf, 0x9a, 0xb6, 0xea, 0x6f, 0x7e, 0xc9, + 0x82, 0x4b, 0x62, 0x02, 0xfc, 0xa0, 0x81, 0x2d, 0xf5, 0x6e, 0x61, 0x6d, 0x09, 0xdf, 0x92, 0xbf, + 0x7e, 0x65, 0x7f, 0x6d, 0x9d, 0x14, 0x6c, 0x1e, 0xbe, 0xff, 0xfe, 0xeb, 0xd3, 0xc6, 0x01, 0xac, + 0xe3, 0xc5, 0x8f, 0xcc, 0xdb, 0xd4, 0xc3, 0x79, 0x97, 0x00, 0x4a, 0x8e, 0xcc, 0x7e, 0x85, 0x9c, + 0xf4, 0x0b, 0x5d, 0x25, 0x67, 0xee, 0x65, 0xfc, 0x93, 0x1c, 0x29, 0xe1, 0x0d, 0xc8, 0xc9, 0x40, + 0xe1, 0xed, 0x3f, 0x0d, 0x99, 0xdb, 0x5c, 0xa5, 0xb6, 0xae, 0x4c, 0x49, 0xb9, 0x25, 0xa4, 0x5c, + 0x87, 0xe5, 0x25, 0x52, 0xe4, 0x8e, 0xac, 0xc7, 0xa7, 0x63, 0x5d, 0x3b, 0x1b, 0xeb, 0xda, 0xcf, + 0xb1, 0xae, 0x7d, 0x9c, 0xe8, 0x99, 0xb3, 0x89, 0x9e, 0xf9, 0x31, 0xd1, 0x33, 0xcf, 0x0e, 0xdd, + 0x6e, 0xf8, 0x6a, 0xe0, 0xa0, 0x36, 0xeb, 0xab, 0xf6, 0x3b, 0x3d, 0xe2, 0xf0, 0x84, 0x6a, 0x78, + 0x84, 0x5f, 0xcf, 0xf8, 0xc2, 0x91, 0x4f, 0xb9, 0x93, 0x13, 0xdf, 0xd6, 0xa3, 0xdf, 0x01, 0x00, + 0x00, 0xff, 0xff, 0xc7, 0x23, 0x51, 0x84, 0xb5, 0x06, 0x00, 0x00, +} + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ grpc.ClientConn + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +const _ = grpc.SupportPackageIsVersion4 + +// QueryClient is the client API for Query service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. +type QueryClient interface { + // Reports allows to query the reports for a specific target + Reports(ctx context.Context, in *QueryReportsRequest, opts ...grpc.CallOption) (*QueryReportsResponse, error) + // Reasons allows to query the supported reporting reasons for a subspace + Reasons(ctx context.Context, in *QueryReasonsRequest, opts ...grpc.CallOption) (*QueryReasonsResponse, error) + // Params allows to query the module parameters + Params(ctx context.Context, in *QueryParamsRequest, opts ...grpc.CallOption) (*QueryParamsResponse, error) +} + +type queryClient struct { + cc grpc1.ClientConn +} + +func NewQueryClient(cc grpc1.ClientConn) QueryClient { + return &queryClient{cc} +} + +func (c *queryClient) Reports(ctx context.Context, in *QueryReportsRequest, opts ...grpc.CallOption) (*QueryReportsResponse, error) { + out := new(QueryReportsResponse) + err := c.cc.Invoke(ctx, "/desmos.reports.v1.Query/Reports", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) Reasons(ctx context.Context, in *QueryReasonsRequest, opts ...grpc.CallOption) (*QueryReasonsResponse, error) { + out := new(QueryReasonsResponse) + err := c.cc.Invoke(ctx, "/desmos.reports.v1.Query/Reasons", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) Params(ctx context.Context, in *QueryParamsRequest, opts ...grpc.CallOption) (*QueryParamsResponse, error) { + out := new(QueryParamsResponse) + err := c.cc.Invoke(ctx, "/desmos.reports.v1.Query/Params", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// QueryServer is the server API for Query service. +type QueryServer interface { + // Reports allows to query the reports for a specific target + Reports(context.Context, *QueryReportsRequest) (*QueryReportsResponse, error) + // Reasons allows to query the supported reporting reasons for a subspace + Reasons(context.Context, *QueryReasonsRequest) (*QueryReasonsResponse, error) + // Params allows to query the module parameters + Params(context.Context, *QueryParamsRequest) (*QueryParamsResponse, error) +} + +// UnimplementedQueryServer can be embedded to have forward compatible implementations. +type UnimplementedQueryServer struct { +} + +func (*UnimplementedQueryServer) Reports(ctx context.Context, req *QueryReportsRequest) (*QueryReportsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Reports not implemented") +} +func (*UnimplementedQueryServer) Reasons(ctx context.Context, req *QueryReasonsRequest) (*QueryReasonsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Reasons not implemented") +} +func (*UnimplementedQueryServer) Params(ctx context.Context, req *QueryParamsRequest) (*QueryParamsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Params not implemented") +} + +func RegisterQueryServer(s grpc1.Server, srv QueryServer) { + s.RegisterService(&_Query_serviceDesc, srv) +} + +func _Query_Reports_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryReportsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).Reports(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/desmos.reports.v1.Query/Reports", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).Reports(ctx, req.(*QueryReportsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_Reasons_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryReasonsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).Reasons(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/desmos.reports.v1.Query/Reasons", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).Reasons(ctx, req.(*QueryReasonsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_Params_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryParamsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).Params(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/desmos.reports.v1.Query/Params", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).Params(ctx, req.(*QueryParamsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +var _Query_serviceDesc = grpc.ServiceDesc{ + ServiceName: "desmos.reports.v1.Query", + HandlerType: (*QueryServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "Reports", + Handler: _Query_Reports_Handler, + }, + { + MethodName: "Reasons", + Handler: _Query_Reasons_Handler, + }, + { + MethodName: "Params", + Handler: _Query_Params_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "desmos/reports/v1/query.proto", +} + +func (m *QueryReportsRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *QueryReportsRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryReportsRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Pagination != nil { + { + size, err := m.Pagination.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + } + if len(m.Reporter) > 0 { + i -= len(m.Reporter) + copy(dAtA[i:], m.Reporter) + i = encodeVarintQuery(dAtA, i, uint64(len(m.Reporter))) + i-- + dAtA[i] = 0x1a + } + if m.Target != nil { + { + size, err := m.Target.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if m.SubspaceId != 0 { + i = encodeVarintQuery(dAtA, i, uint64(m.SubspaceId)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *QueryReportsResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *QueryReportsResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryReportsResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Pagination != nil { + { + size, err := m.Pagination.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if len(m.Reports) > 0 { + for iNdEx := len(m.Reports) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Reports[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *QueryReasonsRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *QueryReasonsRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryReasonsRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Pagination != nil { + { + size, err := m.Pagination.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + if m.SubspaceId != 0 { + i = encodeVarintQuery(dAtA, i, uint64(m.SubspaceId)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *QueryReasonsResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *QueryReasonsResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryReasonsResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Pagination != nil { + { + size, err := m.Pagination.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if len(m.Reasons) > 0 { + for iNdEx := len(m.Reasons) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Reasons[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *QueryParamsRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *QueryParamsRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryParamsRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *QueryParamsResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *QueryParamsResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryParamsResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.Params.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func encodeVarintQuery(dAtA []byte, offset int, v uint64) int { + offset -= sovQuery(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *QueryReportsRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.SubspaceId != 0 { + n += 1 + sovQuery(uint64(m.SubspaceId)) + } + if m.Target != nil { + l = m.Target.Size() + n += 1 + l + sovQuery(uint64(l)) + } + l = len(m.Reporter) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + if m.Pagination != nil { + l = m.Pagination.Size() + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryReportsResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Reports) > 0 { + for _, e := range m.Reports { + l = e.Size() + n += 1 + l + sovQuery(uint64(l)) + } + } + if m.Pagination != nil { + l = m.Pagination.Size() + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryReasonsRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.SubspaceId != 0 { + n += 1 + sovQuery(uint64(m.SubspaceId)) + } + if m.Pagination != nil { + l = m.Pagination.Size() + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryReasonsResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Reasons) > 0 { + for _, e := range m.Reasons { + l = e.Size() + n += 1 + l + sovQuery(uint64(l)) + } + } + if m.Pagination != nil { + l = m.Pagination.Size() + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryParamsRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *QueryParamsResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.Params.Size() + n += 1 + l + sovQuery(uint64(l)) + return n +} + +func sovQuery(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozQuery(x uint64) (n int) { + return sovQuery(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *QueryReportsRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryReportsRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryReportsRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field SubspaceId", wireType) + } + m.SubspaceId = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.SubspaceId |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Target", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Target == nil { + m.Target = &types.Any{} + } + if err := m.Target.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Reporter", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Reporter = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Pagination", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Pagination == nil { + m.Pagination = &query.PageRequest{} + } + if err := m.Pagination.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryReportsResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryReportsResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryReportsResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Reports", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Reports = append(m.Reports, Report{}) + if err := m.Reports[len(m.Reports)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Pagination", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Pagination == nil { + m.Pagination = &query.PageResponse{} + } + if err := m.Pagination.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryReasonsRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryReasonsRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryReasonsRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field SubspaceId", wireType) + } + m.SubspaceId = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.SubspaceId |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Pagination", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Pagination == nil { + m.Pagination = &query.PageRequest{} + } + if err := m.Pagination.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryReasonsResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryReasonsResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryReasonsResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Reasons", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Reasons = append(m.Reasons, Reason{}) + if err := m.Reasons[len(m.Reasons)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Pagination", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Pagination == nil { + m.Pagination = &query.PageResponse{} + } + if err := m.Pagination.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryParamsRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryParamsRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryParamsRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryParamsResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryParamsResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryParamsResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Params", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Params.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipQuery(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowQuery + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowQuery + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowQuery + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthQuery + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupQuery + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthQuery + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthQuery = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowQuery = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupQuery = fmt.Errorf("proto: unexpected end of group") +) diff --git a/x/reports/types/query.pb.gw.go b/x/reports/types/query.pb.gw.go new file mode 100644 index 0000000000..a783e9111c --- /dev/null +++ b/x/reports/types/query.pb.gw.go @@ -0,0 +1,380 @@ +// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT. +// source: desmos/reports/v1/query.proto + +/* +Package types is a reverse proxy. + +It translates gRPC into RESTful JSON APIs. +*/ +package types + +import ( + "context" + "io" + "net/http" + + "github.com/golang/protobuf/descriptor" + "github.com/golang/protobuf/proto" + "github.com/grpc-ecosystem/grpc-gateway/runtime" + "github.com/grpc-ecosystem/grpc-gateway/utilities" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/grpclog" + "google.golang.org/grpc/status" +) + +// Suppress "imported and not used" errors +var _ codes.Code +var _ io.Reader +var _ status.Status +var _ = runtime.String +var _ = utilities.NewDoubleArray +var _ = descriptor.ForMessage + +var ( + filter_Query_Reports_0 = &utilities.DoubleArray{Encoding: map[string]int{"subspace_id": 0}, Base: []int{1, 1, 0}, Check: []int{0, 1, 2}} +) + +func request_Query_Reports_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryReportsRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["subspace_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "subspace_id") + } + + protoReq.SubspaceId, err = runtime.Uint64(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "subspace_id", err) + } + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_Reports_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.Reports(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_Reports_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryReportsRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["subspace_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "subspace_id") + } + + protoReq.SubspaceId, err = runtime.Uint64(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "subspace_id", err) + } + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_Reports_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.Reports(ctx, &protoReq) + return msg, metadata, err + +} + +var ( + filter_Query_Reasons_0 = &utilities.DoubleArray{Encoding: map[string]int{"subspace_id": 0}, Base: []int{1, 1, 0}, Check: []int{0, 1, 2}} +) + +func request_Query_Reasons_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryReasonsRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["subspace_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "subspace_id") + } + + protoReq.SubspaceId, err = runtime.Uint64(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "subspace_id", err) + } + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_Reasons_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.Reasons(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_Reasons_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryReasonsRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["subspace_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "subspace_id") + } + + protoReq.SubspaceId, err = runtime.Uint64(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "subspace_id", err) + } + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_Reasons_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.Reasons(ctx, &protoReq) + return msg, metadata, err + +} + +func request_Query_Params_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryParamsRequest + var metadata runtime.ServerMetadata + + msg, err := client.Params(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_Params_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryParamsRequest + var metadata runtime.ServerMetadata + + msg, err := server.Params(ctx, &protoReq) + return msg, metadata, err + +} + +// RegisterQueryHandlerServer registers the http handlers for service Query to "mux". +// UnaryRPC :call QueryServer directly. +// StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. +// Note that using this registration option will cause many gRPC library features (such as grpc.SendHeader, etc) to stop working. Consider using RegisterQueryHandlerFromEndpoint instead. +func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, server QueryServer) error { + + mux.Handle("GET", pattern_Query_Reports_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_Query_Reports_0(rctx, inboundMarshaler, server, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_Reports_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_Reasons_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_Query_Reasons_0(rctx, inboundMarshaler, server, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_Reasons_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_Params_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_Query_Params_0(rctx, inboundMarshaler, server, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_Params_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + return nil +} + +// RegisterQueryHandlerFromEndpoint is same as RegisterQueryHandler but +// automatically dials to "endpoint" and closes the connection when "ctx" gets done. +func RegisterQueryHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) { + conn, err := grpc.Dial(endpoint, opts...) + if err != nil { + return err + } + defer func() { + if err != nil { + if cerr := conn.Close(); cerr != nil { + grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr) + } + return + } + go func() { + <-ctx.Done() + if cerr := conn.Close(); cerr != nil { + grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr) + } + }() + }() + + return RegisterQueryHandler(ctx, mux, conn) +} + +// RegisterQueryHandler registers the http handlers for service Query to "mux". +// The handlers forward requests to the grpc endpoint over "conn". +func RegisterQueryHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error { + return RegisterQueryHandlerClient(ctx, mux, NewQueryClient(conn)) +} + +// RegisterQueryHandlerClient registers the http handlers for service Query +// to "mux". The handlers forward requests to the grpc endpoint over the given implementation of "QueryClient". +// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "QueryClient" +// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in +// "QueryClient" to call the correct interceptors. +func RegisterQueryHandlerClient(ctx context.Context, mux *runtime.ServeMux, client QueryClient) error { + + mux.Handle("GET", pattern_Query_Reports_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_Query_Reports_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_Reports_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_Reasons_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_Query_Reasons_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_Reasons_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_Params_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_Query_Params_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_Params_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + return nil +} + +var ( + pattern_Query_Reports_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 1, 5, 3, 2, 1}, []string{"desmos", "reports", "v1", "subspace_id"}, "", runtime.AssumeColonVerbOpt(true))) + + pattern_Query_Reasons_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 1, 5, 3, 2, 4}, []string{"desmos", "reports", "v1", "subspace_id", "reasons"}, "", runtime.AssumeColonVerbOpt(true))) + + pattern_Query_Params_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"desmos", "reports", "v1", "params"}, "", runtime.AssumeColonVerbOpt(true))) +) + +var ( + forward_Query_Reports_0 = runtime.ForwardResponseMessage + + forward_Query_Reasons_0 = runtime.ForwardResponseMessage + + forward_Query_Params_0 = runtime.ForwardResponseMessage +) diff --git a/x/subspaces/keeper/invariants.go b/x/subspaces/keeper/invariants.go index c356eacb45..0c8ef6ada2 100644 --- a/x/subspaces/keeper/invariants.go +++ b/x/subspaces/keeper/invariants.go @@ -65,13 +65,13 @@ func ValidSubspacesInvariant(k Keeper) sdk.Invariant { }) return sdk.FormatInvariant(types.ModuleName, "invalid subspaces", - fmt.Sprintf("the following subspaces are invalid:\n%s", formatOutputSubspaces(invalidSubspaces)), + fmt.Sprintf("the following subspaces are invalid:\n %s", FormatOutputSubspaces(invalidSubspaces)), ), invalidSubspaces != nil } } -// formatOutputSubspaces concatenates the given subspaces info into a string -func formatOutputSubspaces(subspaces []types.Subspace) (outputSubspaces string) { +// FormatOutputSubspaces concatenate the subspaces given into a unique string +func FormatOutputSubspaces(subspaces []types.Subspace) (outputSubspaces string) { for _, subspace := range subspaces { outputSubspaces += fmt.Sprintf("%d\n", subspace.ID) } diff --git a/x/subspaces/simulation/decoder.go b/x/subspaces/simulation/decoder.go index e937417050..4b4820f379 100644 --- a/x/subspaces/simulation/decoder.go +++ b/x/subspaces/simulation/decoder.go @@ -25,7 +25,7 @@ func NewDecodeStore(cdc codec.Codec) func(kvA, kvB kv.Pair) string { var subspaceA, subspaceB types.Subspace cdc.MustUnmarshal(kvA.Value, &subspaceA) cdc.MustUnmarshal(kvB.Value, &subspaceB) - return fmt.Sprintf("SubspaceA: %s\nSubspaceB: %s\n", subspaceA.String(), subspaceB.String()) + return fmt.Sprintf("SubspaceA: %s\nSubspaceB: %s\n", &subspaceA, &subspaceB) case bytes.HasPrefix(kvA.Key, types.GroupIDPrefix): var groupIDA, groupIDB uint32 diff --git a/x/subspaces/types/genesis_test.go b/x/subspaces/types/genesis_test.go index db929db330..3821fbcb50 100644 --- a/x/subspaces/types/genesis_test.go +++ b/x/subspaces/types/genesis_test.go @@ -300,7 +300,7 @@ func TestUserPermission_Validate(t *testing.T) { }, { name: "invalid permission returns error", - entry: types.NewUserPermission(1, 0, "cosmos15p3m7a93luselt80ffzpf4jwtn9ama34ray0nd", 512), + entry: types.NewUserPermission(1, 0, "cosmos15p3m7a93luselt80ffzpf4jwtn9ama34ray0nd", 9999), shouldErr: true, }, { diff --git a/x/subspaces/types/permissions.go b/x/subspaces/types/permissions.go index 990d901e48..fc2602bf37 100644 --- a/x/subspaces/types/permissions.go +++ b/x/subspaces/types/permissions.go @@ -11,19 +11,19 @@ type Permission = uint32 const ( // PermissionNothing represents the permission to do nothing - PermissionNothing = Permission(0b000000) + PermissionNothing = Permission(0) // PermissionWrite identifies users that can create content inside the subspace - PermissionWrite = Permission(0b000001) + PermissionWrite = Permission(1) // PermissionModerateContent allows users to moderate contents of other users (e.g. deleting it) - PermissionModerateContent = Permission(0b000010) + PermissionModerateContent = Permission(0b010) // PermissionChangeInfo allows to change the information of the subspace - PermissionChangeInfo = Permission(0b000100) + PermissionChangeInfo = Permission(0b0100) // PermissionManageGroups allows users to manage user groups and members - PermissionManageGroups = Permission(0b001000) + PermissionManageGroups = Permission(0b01000) // PermissionSetPermissions allows users to set other users' permissions (except PermissionSetPermissions). // This includes managing user groups and the associated permissions @@ -41,20 +41,39 @@ const ( // PermissionEditOwnContent allows users to edit their own content inside the subspace PermissionEditOwnContent = Permission(0b100000000) + // PermissionReportContent allows users to report contents + PermissionReportContent = Permission(0b1000000000) + + // PermissionDeleteOwnReports allows users to delete existing reports made by their own + PermissionDeleteOwnReports = Permission(0b10000000000) + + // PermissionManageReports allows users to manage other users reports + PermissionManageReports = Permission(0b100000000000) + + // PermissionManageReasons allows users to manage a subspace reasons for reporting + PermissionManageReasons = Permission(0b1000000000000) + // PermissionEverything allows to do everything. // This should usually be reserved only to the owner (which has it by default) - PermissionEverything = Permission(0b111111111) + PermissionEverything = Permission(0b1111111111111) ) var ( permissionsMap = map[Permission]string{ - PermissionNothing: "Nothing", - PermissionWrite: "Write", - PermissionModerateContent: "ModerateContent", - PermissionChangeInfo: "ChangeInfo", - PermissionManageGroups: "ManageGroups", - PermissionSetPermissions: "SetUserPermissions", - PermissionEverything: "Everything", + PermissionNothing: "Nothing", + PermissionWrite: "Write", + PermissionModerateContent: "ModerateContent", + PermissionChangeInfo: "ChangeInfo", + PermissionManageGroups: "ManageGroups", + PermissionSetPermissions: "SetPermissions", + PermissionDeleteSubspace: "DeleteSubspace", + PermissionInteractWithContent: "InteractWithContent", + PermissionEditOwnContent: "EditOwnContent", + PermissionReportContent: "ReportContent", + PermissionDeleteOwnReports: "DeleteOwnReports", + PermissionManageReports: "ManageReports", + PermissionManageReasons: "ManageReasons", + PermissionEverything: "Everything", } ) diff --git a/x/subspaces/types/permissions_test.go b/x/subspaces/types/permissions_test.go index 8472dac9c5..f6c29ca8e2 100644 --- a/x/subspaces/types/permissions_test.go +++ b/x/subspaces/types/permissions_test.go @@ -173,13 +173,13 @@ func TestSanitizePermission(t *testing.T) { expResult: types.PermissionWrite & types.PermissionChangeInfo, }, { - name: "invalid permission returns permission nothing", - permission: 1024, - expResult: types.PermissionNothing, + name: "invalid permission returns permission everything", + permission: 4294967295, + expResult: types.PermissionEverything, }, { name: "extra bits are set to 0", - permission: 0b11111111111111111111111000000001, + permission: 0b11111111111111111110000000000001, expResult: types.PermissionWrite, }, } @@ -211,7 +211,7 @@ func TestIsPermissionValid(t *testing.T) { }, { name: "invalid permission returns false", - permission: 1024, + permission: 99999, expValid: false, }, {