diff --git a/Makefile b/Makefile index 28b58e15e..ba08bdf84 100644 --- a/Makefile +++ b/Makefile @@ -282,6 +282,22 @@ app2_unstake: ## Unstake app2 app3_unstake: ## Unstake app3 APP=app3 make app_unstake +.PHONY: app_delegate +app_delegate: ## Delegate trust to a gateway (must specify the APP and GATEWAY_ADDR env vars). Requires the app to be staked + pocketd --home=$(POCKETD_HOME) tx application delegate-to-gateway $(GATEWAY_ADDR) --keyring-backend test --from $(APP) --node $(POCKET_NODE) + +.PHONY: app1_delegate_gateway1 +app1_delegate_gateway1: ## Delegate trust to gateway1 + APP=app1 GATEWAY_ADDR=pokt15vzxjqklzjtlz7lahe8z2dfe9nm5vxwwmscne4 make app_delegate + +.PHONY: app2_delegate_gateway2 +app2_delegate_gateway2: ## Delegate trust to gateway2 + APP=app2 GATEWAY_ADDR=pokt15w3fhfyc0lttv7r585e2ncpf6t2kl9uh8rsnyz make app_delegate + +.PHONY: app3_delegate_gateway3 +app3_delegate_gateway3: ## Delegate trust to gateway3 + APP=app3 GATEWAY_ADDR=pokt1zhmkkd0rh788mc9prfq0m2h88t9ge0j83gnxya make app_delegate + ################# ### Suppliers ### ################# diff --git a/app/app.go b/app/app.go index f640a6ee7..05cad6ae7 100644 --- a/app/app.go +++ b/app/app.go @@ -6,6 +6,7 @@ import ( "io" "os" "path/filepath" + // this line is used by starport scaffolding # stargate/app/moduleImport autocliv1 "cosmossdk.io/api/cosmos/autocli/v1" @@ -585,16 +586,6 @@ func New( ) sessionModule := sessionmodule.NewAppModule(appCodec, app.SessionKeeper, app.AccountKeeper, app.BankKeeper) - app.ApplicationKeeper = *applicationmodulekeeper.NewKeeper( - appCodec, - keys[applicationmoduletypes.StoreKey], - keys[applicationmoduletypes.MemStoreKey], - app.GetSubspace(applicationmoduletypes.ModuleName), - - app.BankKeeper, - ) - applicationModule := applicationmodule.NewAppModule(appCodec, app.ApplicationKeeper, app.AccountKeeper, app.BankKeeper) - app.SupplierKeeper = *suppliermodulekeeper.NewKeeper( appCodec, keys[suppliermoduletypes.StoreKey], @@ -612,10 +603,21 @@ func New( app.GetSubspace(gatewaymoduletypes.ModuleName), app.BankKeeper, - app.AccountKeeper, ) gatewayModule := gatewaymodule.NewAppModule(appCodec, app.GatewayKeeper, app.AccountKeeper, app.BankKeeper) + app.ApplicationKeeper = *applicationmodulekeeper.NewKeeper( + appCodec, + keys[applicationmoduletypes.StoreKey], + keys[applicationmoduletypes.MemStoreKey], + app.GetSubspace(applicationmoduletypes.ModuleName), + + app.BankKeeper, + app.AccountKeeper, + app.GatewayKeeper, + ) + applicationModule := applicationmodule.NewAppModule(appCodec, app.ApplicationKeeper, app.AccountKeeper, app.BankKeeper) + // this line is used by starport scaffolding # stargate/app/keeperDefinition /**** IBC Routing ****/ diff --git a/docs/static/openapi.yml b/docs/static/openapi.yml index 461aaedf0..3e0e46e33 100644 --- a/docs/static/openapi.yml +++ b/docs/static/openapi.yml @@ -46489,7 +46489,9 @@ paths: was desigtned created to enable more complex service identification - gener + For example, what if we want to request a + session for a certain service but with some + additional configs that identify it? name: type: string description: >- @@ -46503,6 +46505,13 @@ paths: ApplicationServiceConfig holds the service configuration the application stakes for title: The ID of the service this session is servicing + delegatee_gateway_addresses: + type: array + items: + type: string + title: >- + The Bech32 encoded addresses for all delegatee Gateways, + in a non-nullable slice title: >- Application defines the type used to store an on-chain definition and state for an application @@ -46662,7 +46671,9 @@ paths: desigtned created to enable more complex service identification - gener + For example, what if we want to request a + session for a certain service but with some + additional configs that identify it? name: type: string description: >- @@ -46676,6 +46687,13 @@ paths: ApplicationServiceConfig holds the service configuration the application stakes for title: The ID of the service this session is servicing + delegatee_gateway_addresses: + type: array + items: + type: string + title: >- + The Bech32 encoded addresses for all delegatee Gateways, + in a non-nullable slice title: >- Application defines the type used to store an on-chain definition and state for an application @@ -47176,6 +47194,189 @@ paths: ApplicationServiceConfig holds the service configuration the application stakes for title: The ID of the service this session is servicing + delegatee_gateway_pub_keys: + type: array + items: + type: object + properties: + '@type': + 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. + additionalProperties: {} + 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 := anypb.New(foo) + if err != nil { + ... + } + ... + foo := &pb.Foo{} + if err := any.UnmarshalTo(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: >- + The corresponding cosmos.crypto.PubKey (interface) + encoded into an cosmos.codec.Any for use in the non + nullable slice of delegatee Gateways the application + is delegated to. suppliers: type: array items: @@ -47322,147 +47523,648 @@ paths: properties: '@type': type: string - additionalProperties: {} - parameters: - - name: application_address - description: >- - The Bech32 address of the application using cosmos' ScalarDescriptor - to ensure deterministic encoding - in: query - required: false - type: string - - name: service_id.id - description: >- - NOTE: `ServiceId.Id` may seem redundant but was desigtned created to - enable more complex service identification + description: >- + A URL/resource name that uniquely identifies the type of + the serialized - For example, what if we want to request a session for a certain - service but with some additional configs that identify it? + protocol buffer message. This string must contain at + least + one "/" character. The last segment of the URL's path + must represent - Unique identifier for the service - in: query - required: false - type: string - - name: service_id.name - description: >- - TODO_TECHDEBT: Name is currently unused but acts as a reminder than - an optional onchain representation of the service is necessary + the fully qualified name of the type (as in + `path/google.protobuf.Duration`). The name should be in + a canonical form - (Optional) Semantic human readable name for the service - in: query - required: false - type: string - - name: block_height - description: The block height to query the session for - in: query - required: false - type: string - format: int64 - tags: - - Query - /pocket/session/params: - get: - summary: Parameters queries the parameters of the module. - operationId: PocketSessionParams - responses: - '200': - description: A successful response. - schema: - type: object - properties: - params: - description: params holds all the parameters of this module. - type: object - description: >- - QueryParamsResponse is response type for the Query/Params RPC - method. - default: - description: An unexpected error response. - schema: - type: object - properties: - code: - type: integer - format: int32 - message: - type: string - details: - type: array - items: - type: object - properties: - '@type': - type: string - additionalProperties: {} - tags: - - Query - /pocket/supplier/params: - get: - summary: Parameters queries the parameters of the module. - operationId: PocketSupplierParams - responses: - '200': - description: A successful response. - schema: - type: object - properties: - params: - description: params holds all the parameters of this module. - type: object - description: >- - QueryParamsResponse is response type for the Query/Params RPC - method. - default: - description: An unexpected error response. - schema: - type: object - properties: - code: - type: integer - format: int32 - message: - type: string - details: - type: array - items: - type: object - properties: - '@type': - type: string - additionalProperties: {} - tags: - - Query - /pocket/supplier/supplier: - get: - operationId: PocketSupplierSupplierAll - responses: - '200': - description: A successful response. - schema: - type: object - properties: - supplier: - type: array - items: - type: object - properties: - address: - type: string - title: >- - The Bech32 address of the supplier using cosmos' - ScalarDescriptor to ensure deterministic encoding - stake: - title: The total amount of uPOKT the supplier has staked - type: object - properties: - denom: - type: string - amount: - type: string - description: >- - Coin defines a token with a denomination and an amount. + (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. + additionalProperties: {} + 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 := anypb.New(foo) + if err != nil { + ... + } + ... + foo := &pb.Foo{} + if err := any.UnmarshalTo(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" + } + parameters: + - name: application_address + description: >- + The Bech32 address of the application using cosmos' ScalarDescriptor + to ensure deterministic encoding + in: query + required: false + type: string + - name: service_id.id + description: >- + NOTE: `ServiceId.Id` may seem redundant but was desigtned created to + enable more complex service identification + + For example, what if we want to request a session for a certain + service but with some additional configs that identify it? + + + Unique identifier for the service + in: query + required: false + type: string + - name: service_id.name + description: >- + TODO_TECHDEBT: Name is currently unused but acts as a reminder than + an optional onchain representation of the service is necessary + + + (Optional) Semantic human readable name for the service + in: query + required: false + type: string + - name: block_height + description: The block height to query the session for + in: query + required: false + type: string + format: int64 + tags: + - Query + /pocket/session/params: + get: + summary: Parameters queries the parameters of the module. + operationId: PocketSessionParams + responses: + '200': + description: A successful response. + schema: + type: object + properties: + params: + description: params holds all the parameters of this module. + type: object + description: >- + QueryParamsResponse is response type for the Query/Params RPC + method. + default: + description: An unexpected error response. + schema: + type: object + properties: + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + '@type': + 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. + additionalProperties: {} + 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 := anypb.New(foo) + if err != nil { + ... + } + ... + foo := &pb.Foo{} + if err := any.UnmarshalTo(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" + } + tags: + - Query + /pocket/supplier/params: + get: + summary: Parameters queries the parameters of the module. + operationId: PocketSupplierParams + responses: + '200': + description: A successful response. + schema: + type: object + properties: + params: + description: params holds all the parameters of this module. + type: object + description: >- + QueryParamsResponse is response type for the Query/Params RPC + method. + default: + description: An unexpected error response. + schema: + type: object + properties: + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + '@type': + 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. + additionalProperties: {} + 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 := anypb.New(foo) + if err != nil { + ... + } + ... + foo := &pb.Foo{} + if err := any.UnmarshalTo(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" + } + tags: + - Query + /pocket/supplier/supplier: + get: + operationId: PocketSupplierSupplierAll + responses: + '200': + description: A successful response. + schema: + type: object + properties: + supplier: + type: array + items: + type: object + properties: + address: + type: string + title: >- + The Bech32 address of the supplier using cosmos' + ScalarDescriptor to ensure deterministic encoding + stake: + title: The total amount of uPOKT the supplier has staked + 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 @@ -47480,10 +48182,24 @@ paths: properties: id: type: string - title: Unique identifier for the service + description: Unique identifier for the service + title: >- + NOTE: `ServiceId.Id` may seem redundant but + was desigtned created to enable more complex + service identification + + For example, what if we want to request a + session for a certain service but with some + additional configs that identify it? name: type: string - title: Semantic name for the service + description: >- + (Optional) Semantic human readable name for + the service + title: >- + TODO_TECHDEBT: Name is currently unused but + acts as a reminder than an optional onchain + representation of the service is necessary endpoints: type: array items: @@ -47538,8 +48254,8 @@ paths: Additional configuration options for the endpoint title: >- - Endpoint message to hold service configuration - details + SupplierEndpoint message to hold service + configuration details title: List of endpoints for the service title: >- SupplierServiceConfig holds the service configuration @@ -47565,35 +48281,202 @@ paths: 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 + 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; + } + default: + description: An unexpected error response. + schema: + type: object + properties: + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + '@type': + 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. + additionalProperties: {} + 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 := anypb.New(foo) + if err != nil { + ... + } + ... + foo := &pb.Foo{} + if err := any.UnmarshalTo(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 - corresponding request message has used PageRequest. + name "y.z". - message SomeResponse { - repeated Bar results = 1; - PageResponse page = 2; - } - default: - description: An unexpected error response. - schema: - type: object - properties: - code: - type: integer - format: int32 - message: - type: string - details: - type: array - items: - type: object - properties: - '@type': - type: string - additionalProperties: {} + + + 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" + } parameters: - name: pagination.key description: |- @@ -47698,10 +48581,24 @@ paths: properties: id: type: string - title: Unique identifier for the service + description: Unique identifier for the service + title: >- + NOTE: `ServiceId.Id` may seem redundant but was + desigtned created to enable more complex service + identification + + For example, what if we want to request a + session for a certain service but with some + additional configs that identify it? name: type: string - title: Semantic name for the service + description: >- + (Optional) Semantic human readable name for the + service + title: >- + TODO_TECHDEBT: Name is currently unused but acts + as a reminder than an optional onchain + representation of the service is necessary endpoints: type: array items: @@ -47739,51 +48636,218 @@ paths: description: >- Enum to define configuration options - TODO_RESEARCH: Should these be configs, - SLAs or something else? There will be - more discussion once we get closer to - implementing on-chain QoS. + TODO_RESEARCH: Should these be configs, + SLAs or something else? There will be + more discussion once we get closer to + implementing on-chain QoS. + + - UNKNOWN_CONFIG: Undefined config option + - TIMEOUT: Timeout setting + value: + type: string + title: Config option value + title: >- + Key-value wrapper for config options, as + proto maps can't be keyed by enums + title: >- + Additional configuration options for the + endpoint + title: >- + SupplierEndpoint message to hold service + configuration details + title: List of endpoints for the service + title: >- + SupplierServiceConfig holds the service configuration + the supplier stakes for + title: The service configs this supplier can support + description: >- + Supplier is the type defining the actor in Pocket Network that + provides RPC services. + default: + description: An unexpected error response. + schema: + type: object + properties: + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + '@type': + 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. + additionalProperties: {} + 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 := anypb.New(foo) + if err != nil { + ... + } + ... + foo := &pb.Foo{} + if err := any.UnmarshalTo(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 + + ==== - - UNKNOWN_CONFIG: Undefined config option - - TIMEOUT: Timeout setting - value: - type: string - title: Config option value - title: >- - Key-value wrapper for config options, as - proto maps can't be keyed by enums - title: >- - Additional configuration options for the - endpoint - title: >- - Endpoint message to hold service configuration - details - title: List of endpoints for the service - title: >- - SupplierServiceConfig holds the service configuration - the supplier stakes for - title: The service configs this supplier can support - description: >- - Supplier is the type defining the actor in Pocket Network that - provides RPC services. - default: - description: An unexpected error response. - schema: - type: object - properties: - code: - type: integer - format: int32 - message: - type: string - details: - type: array - items: - type: object - properties: - '@type': - type: string - additionalProperties: {} + 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" + } parameters: - name: address in: path @@ -76549,36 +77613,211 @@ definitions: 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. - service_configs: - type: array - items: - type: object - properties: - service_id: - title: Unique and semantic identifier for the service - type: object - properties: - id: - type: string - description: Unique identifier for the service - title: >- - NOTE: `ServiceId.Id` may seem redundant but was desigtned - created to enable more complex service identification + NOTE: The amount field is an Int which implements the custom method + signatures required by gogoproto. + service_configs: + type: array + items: + type: object + properties: + service_id: + title: Unique and semantic identifier for the service + type: object + properties: + id: + type: string + description: Unique identifier for the service + title: >- + NOTE: `ServiceId.Id` may seem redundant but was desigtned + created to enable more complex service identification + + For example, what if we want to request a session for a + certain service but with some additional configs that + identify it? + name: + type: string + description: (Optional) Semantic human readable name for the service + title: >- + TODO_TECHDEBT: Name is currently unused but acts as a + reminder than an optional onchain representation of the + service is necessary + title: >- + ApplicationServiceConfig holds the service configuration the + application stakes for + title: The ID of the service this session is servicing + delegatee_gateway_addresses: + type: array + items: + type: string + title: >- + The Bech32 encoded addresses for all delegatee Gateways, in a + non-nullable slice + delegatee_gateway_pub_keys: + type: array + items: + type: object + properties: + '@type': + 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. + additionalProperties: {} + 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 := anypb.New(foo) + if err != nil { + ... + } + ... + foo := &pb.Foo{} + if err := any.UnmarshalTo(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": + } - gener - name: - type: string - description: (Optional) Semantic human readable name for the service - title: >- - TODO_TECHDEBT: Name is currently unused but acts as a - reminder than an optional onchain representation of the - service is necessary - title: >- - ApplicationServiceConfig holds the service configuration the - application stakes for - title: The ID of the service this session is servicing + 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: >- + The corresponding cosmos.crypto.PubKey (interface) encoded into an + cosmos.codec.Any for use in the non nullable slice of delegatee + Gateways the application is delegated to. title: >- Application defines the type used to store an on-chain definition and state for an application @@ -76639,7 +77878,9 @@ definitions: desigtned created to enable more complex service identification - gener + For example, what if we want to request a session for + a certain service but with some additional configs + that identify it? name: type: string description: >- @@ -76653,6 +77894,13 @@ definitions: ApplicationServiceConfig holds the service configuration the application stakes for title: The ID of the service this session is servicing + delegatee_gateway_addresses: + type: array + items: + type: string + title: >- + The Bech32 encoded addresses for all delegatee Gateways, in a + non-nullable slice title: >- Application defines the type used to store an on-chain definition and state for an application @@ -76726,7 +77974,9 @@ definitions: desigtned created to enable more complex service identification - gener + For example, what if we want to request a session for a + certain service but with some additional configs that + identify it? name: type: string description: (Optional) Semantic human readable name for the service @@ -76738,6 +77988,13 @@ definitions: ApplicationServiceConfig holds the service configuration the application stakes for title: The ID of the service this session is servicing + delegatee_gateway_addresses: + type: array + items: + type: string + title: >- + The Bech32 encoded addresses for all delegatee Gateways, in a + non-nullable slice title: >- Application defines the type used to store an on-chain definition and state for an application @@ -76762,7 +78019,8 @@ definitions: NOTE: `ServiceId.Id` may seem redundant but was desigtned created to enable more complex service identification - gener + For example, what if we want to request a session for a certain + service but with some additional configs that identify it? name: type: string description: (Optional) Semantic human readable name for the service @@ -76783,7 +78041,8 @@ definitions: NOTE: `ServiceId.Id` may seem redundant but was desigtned created to enable more complex service identification - gener + For example, what if we want to request a session for a certain + service but with some additional configs that identify it? name: type: string description: (Optional) Semantic human readable name for the service @@ -77011,43 +78270,222 @@ definitions: 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. + service_configs: + type: array + items: + type: object + properties: + service_id: + title: Unique and semantic identifier for the service + type: object + properties: + id: + type: string + description: Unique identifier for the service + title: >- + NOTE: `ServiceId.Id` may seem redundant but was + desigtned created to enable more complex service + identification + + For example, what if we want to request a session + for a certain service but with some additional + configs that identify it? + name: + type: string + description: >- + (Optional) Semantic human readable name for the + service + title: >- + TODO_TECHDEBT: Name is currently unused but acts as + a reminder than an optional onchain representation + of the service is necessary + title: >- + ApplicationServiceConfig holds the service configuration the + application stakes for + title: The ID of the service this session is servicing + delegatee_gateway_pub_keys: + type: array + items: + type: object + properties: + '@type': + 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. + additionalProperties: {} + 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 := anypb.New(foo) + if err != nil { + ... + } + ... + foo := &pb.Foo{} + if err := any.UnmarshalTo(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 - signatures required by gogoproto. - service_configs: - type: array - items: - type: object - properties: - service_id: - title: Unique and semantic identifier for the service - type: object - properties: - id: - type: string - description: Unique identifier for the service - title: >- - NOTE: `ServiceId.Id` may seem redundant but was - desigtned created to enable more complex service - identification + `value` which holds the custom JSON in addition to the + `@type` - For example, what if we want to request a session - for a certain service but with some additional - configs that identify it? - name: - type: string - description: >- - (Optional) Semantic human readable name for the - service - title: >- - TODO_TECHDEBT: Name is currently unused but acts as - a reminder than an optional onchain representation - of the service is necessary - title: >- - ApplicationServiceConfig holds the service configuration the - application stakes for - title: The ID of the service this session is servicing + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + description: >- + The corresponding cosmos.crypto.PubKey (interface) encoded + into an cosmos.codec.Any for use in the non nullable slice of + delegatee Gateways the application is delegated to. suppliers: type: array items: @@ -77296,6 +78734,179 @@ definitions: ApplicationServiceConfig holds the service configuration the application stakes for title: The ID of the service this session is servicing + delegatee_gateway_pub_keys: + type: array + items: + type: object + properties: + '@type': + 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. + additionalProperties: {} + 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 := anypb.New(foo) + if err != nil { + ... + } + ... + foo := &pb.Foo{} + if err := any.UnmarshalTo(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: >- + The corresponding cosmos.crypto.PubKey (interface) encoded into an + cosmos.codec.Any for use in the non nullable slice of delegatee + Gateways the application is delegated to. suppliers: type: array items: @@ -77800,10 +79411,24 @@ definitions: properties: id: type: string - title: Unique identifier for the service + description: Unique identifier for the service + title: >- + NOTE: `ServiceId.Id` may seem redundant but was + desigtned created to enable more complex service + identification + + For example, what if we want to request a session for + a certain service but with some additional configs + that identify it? name: type: string - title: Semantic name for the service + description: >- + (Optional) Semantic human readable name for the + service + title: >- + TODO_TECHDEBT: Name is currently unused but acts as a + reminder than an optional onchain representation of + the service is necessary endpoints: type: array items: @@ -77855,7 +79480,9 @@ definitions: Key-value wrapper for config options, as proto maps can't be keyed by enums title: Additional configuration options for the endpoint - title: Endpoint message to hold service configuration details + title: >- + SupplierEndpoint message to hold service configuration + details title: List of endpoints for the service title: >- SupplierServiceConfig holds the service configuration the @@ -77928,10 +79555,22 @@ definitions: properties: id: type: string - title: Unique identifier for the service + description: Unique identifier for the service + title: >- + NOTE: `ServiceId.Id` may seem redundant but was + desigtned created to enable more complex service + identification + + For example, what if we want to request a session for a + certain service but with some additional configs that + identify it? name: type: string - title: Semantic name for the service + description: (Optional) Semantic human readable name for the service + title: >- + TODO_TECHDEBT: Name is currently unused but acts as a + reminder than an optional onchain representation of the + service is necessary endpoints: type: array items: @@ -77982,7 +79621,9 @@ definitions: Key-value wrapper for config options, as proto maps can't be keyed by enums title: Additional configuration options for the endpoint - title: Endpoint message to hold service configuration details + title: >- + SupplierEndpoint message to hold service configuration + details title: List of endpoints for the service title: >- SupplierServiceConfig holds the service configuration the diff --git a/go.mod b/go.mod index cb8df3174..d2fd3985c 100644 --- a/go.mod +++ b/go.mod @@ -8,6 +8,7 @@ require ( cosmossdk.io/math v1.0.1 github.com/cometbft/cometbft v0.37.2 github.com/cometbft/cometbft-db v0.8.0 + github.com/cosmos/cosmos-proto v1.0.0-beta.2 github.com/cosmos/cosmos-sdk v0.47.3 github.com/cosmos/gogoproto v1.4.10 github.com/cosmos/ibc-go/v7 v7.1.0 @@ -25,6 +26,7 @@ require ( go.uber.org/multierr v1.11.0 golang.org/x/crypto v0.12.0 golang.org/x/sync v0.3.0 + google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 google.golang.org/grpc v1.56.1 gopkg.in/yaml.v2 v2.4.0 ) @@ -69,7 +71,6 @@ require ( github.com/containerd/cgroups v1.1.0 // indirect github.com/coreos/go-systemd/v22 v22.5.0 // indirect github.com/cosmos/btcutil v1.0.5 // indirect - github.com/cosmos/cosmos-proto v1.0.0-beta.2 // indirect github.com/cosmos/go-bip39 v1.0.0 // indirect github.com/cosmos/gogogateway v1.2.0 // indirect github.com/cosmos/iavl v0.20.0 // indirect @@ -265,7 +266,6 @@ require ( gonum.org/v1/gonum v0.11.0 // indirect google.golang.org/api v0.122.0 // indirect google.golang.org/appengine v1.6.7 // indirect - google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect google.golang.org/protobuf v1.31.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/proto/pocket/application/application.proto b/proto/pocket/application/application.proto index c7b0ae83b..2c754a5dc 100644 --- a/proto/pocket/application/application.proto +++ b/proto/pocket/application/application.proto @@ -5,6 +5,8 @@ option go_package = "pocket/x/application/types"; import "cosmos_proto/cosmos.proto"; import "cosmos/base/v1beta1/coin.proto"; +import "gogoproto/gogo.proto"; + import "pocket/shared/service.proto"; // Application defines the type used to store an on-chain definition and state for an application @@ -12,5 +14,5 @@ message Application { string address = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"]; // The Bech32 address of the application using cosmos' ScalarDescriptor to ensure deterministic encoding cosmos.base.v1beta1.Coin stake = 2; // The total amount of uPOKT the application has staked repeated shared.ApplicationServiceConfig service_configs = 3; // The ID of the service this session is servicing + repeated string delegatee_gateway_addresses = 4 [(cosmos_proto.scalar) = "cosmos.AddressString", (gogoproto.nullable) = false]; // The Bech32 encoded addresses for all delegatee Gateways, in a non-nullable slice } - diff --git a/proto/pocket/application/tx.proto b/proto/pocket/application/tx.proto index 971d47b83..0b49ce706 100644 --- a/proto/pocket/application/tx.proto +++ b/proto/pocket/application/tx.proto @@ -34,7 +34,9 @@ message MsgUnstakeApplication { message MsgUnstakeApplicationResponse {} message MsgDelegateToGateway { - string address = 1; + option (cosmos.msg.v1.signer) = "app_address"; // https://docs.cosmos.network/main/build/building-modules/messages-and-queries + string app_address = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"]; // The Bech32 address of the application using cosmos' ScalarDescriptor to ensure deterministic deterministic encoding using cosmos' ScalarDescriptor to ensure deterministic deterministic encoding + string gateway_address = 2 [(cosmos_proto.scalar) = "cosmos.AddressString"]; // The Bech32 address of the gateway the application wants to delegate to using cosmos' ScalarDescriptor to ensure deterministic deterministic encoding using cosmos' ScalarDescriptor to ensure deterministic deterministic encoding } message MsgDelegateToGatewayResponse {} diff --git a/testutil/keeper/application.go b/testutil/keeper/application.go index 08bf27bb7..71b06b446 100644 --- a/testutil/keeper/application.go +++ b/testutil/keeper/application.go @@ -18,8 +18,14 @@ import ( mocks "pocket/testutil/application/mocks" "pocket/x/application/keeper" "pocket/x/application/types" + gatewaytypes "pocket/x/gateway/types" ) +// StakedGatewayMap is used to mock whether a gateway is staked or not for use +// in the application's mocked gateway keeper. This enables the tester to +// control whether a gateway is "staked" or not and whether it can be delegated to +var StakedGatewayMap = make(map[string]struct{}) + func ApplicationKeeper(t testing.TB) (*keeper.Keeper, sdk.Context) { storeKey := sdk.NewKVStoreKey(types.StoreKey) memStoreKey := storetypes.NewMemoryStoreKey(types.MemStoreKey) @@ -38,6 +44,23 @@ func ApplicationKeeper(t testing.TB) (*keeper.Keeper, sdk.Context) { mockBankKeeper.EXPECT().DelegateCoinsFromAccountToModule(gomock.Any(), gomock.Any(), types.ModuleName, gomock.Any()).AnyTimes() mockBankKeeper.EXPECT().UndelegateCoinsFromModuleToAccount(gomock.Any(), types.ModuleName, gomock.Any(), gomock.Any()).AnyTimes() + mockAccountKeeper := mocks.NewMockAccountKeeper(ctrl) + mockAccountKeeper.EXPECT().GetAccount(gomock.Any(), gomock.Any()).AnyTimes() + + mockGatewayKeeper := mocks.NewMockGatewayKeeper(ctrl) + mockGatewayKeeper.EXPECT().GetGateway(gomock.Any(), gomock.Any()).DoAndReturn( + func(_ sdk.Context, addr string) (gatewaytypes.Gateway, bool) { + if _, ok := StakedGatewayMap[addr]; !ok { + return gatewaytypes.Gateway{}, false + } + stake := sdk.NewCoin("upokt", sdk.NewInt(10000)) + return gatewaytypes.Gateway{ + Address: addr, + Stake: &stake, + }, true + }, + ).AnyTimes() + paramsSubspace := typesparams.NewSubspace(cdc, types.Amino, storeKey, @@ -50,6 +73,8 @@ func ApplicationKeeper(t testing.TB) (*keeper.Keeper, sdk.Context) { memStoreKey, paramsSubspace, mockBankKeeper, + mockAccountKeeper, + mockGatewayKeeper, ) ctx := sdk.NewContext(stateStore, tmproto.Header{}, false, log.NewNopLogger()) diff --git a/testutil/keeper/gateway.go b/testutil/keeper/gateway.go index cd2e3a3eb..2a7f27ea9 100644 --- a/testutil/keeper/gateway.go +++ b/testutil/keeper/gateway.go @@ -51,7 +51,6 @@ func GatewayKeeper(t testing.TB) (*keeper.Keeper, sdk.Context) { memStoreKey, paramsSubspace, mockBankKeeper, - nil, ) ctx := sdk.NewContext(stateStore, tmproto.Header{}, false, log.NewNopLogger()) diff --git a/x/application/client/cli/tx_delegate_to_gateway.go b/x/application/client/cli/tx_delegate_to_gateway.go index 8291df1a5..f993739db 100644 --- a/x/application/client/cli/tx_delegate_to_gateway.go +++ b/x/application/client/cli/tx_delegate_to_gateway.go @@ -7,6 +7,7 @@ import ( "github.com/cosmos/cosmos-sdk/client/flags" "github.com/cosmos/cosmos-sdk/client/tx" "github.com/spf13/cobra" + "pocket/x/application/types" ) @@ -14,11 +15,17 @@ var _ = strconv.Itoa(0) func CmdDelegateToGateway() *cobra.Command { cmd := &cobra.Command{ - Use: "delegate-to-gateway", - Short: "Broadcast message delegate-to-gateway", - Args: cobra.ExactArgs(0), + Use: "delegate-to-gateway [gateway address]", + Short: "Delegate an application to a gateway", + Long: `Delegate an application to the gateway with the provided address. This is a broadcast operation +that delegates authority to the gateway specified to sign relays requests for the application, allowing the gateway +act on the behalf of the application during a session. + +Example: +$ pocketd --home=$(POCKETD_HOME) tx application delegate-to-gateway $(GATEWAY_ADDR) --keyring-backend test --from $(APP) --node $(POCKET_NODE)`, + Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) (err error) { - + gatewayAddress := args[0] clientCtx, err := client.GetClientTxContext(cmd) if err != nil { return err @@ -26,10 +33,12 @@ func CmdDelegateToGateway() *cobra.Command { msg := types.NewMsgDelegateToGateway( clientCtx.GetFromAddress().String(), + gatewayAddress, ) if err := msg.ValidateBasic(); err != nil { return err } + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) }, } diff --git a/x/application/client/cli/tx_delegate_to_gateway_test.go b/x/application/client/cli/tx_delegate_to_gateway_test.go new file mode 100644 index 000000000..b70f4558b --- /dev/null +++ b/x/application/client/cli/tx_delegate_to_gateway_test.go @@ -0,0 +1,117 @@ +package cli_test + +import ( + "fmt" + "testing" + + sdkerrors "cosmossdk.io/errors" + sdkmath "cosmossdk.io/math" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/testutil" + clitestutil "github.com/cosmos/cosmos-sdk/testutil/cli" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/require" + "google.golang.org/grpc/status" + + "pocket/testutil/network" + "pocket/x/application/client/cli" + "pocket/x/application/types" +) + +func TestCLI_DelegateToGateway(t *testing.T) { + net, _ := networkWithApplicationObjects(t, 2) + val := net.Validators[0] + ctx := val.ClientCtx + + // Create a keyring and add an account for the application to be delegated + // and the gateway to be delegated to + kr := ctx.Keyring + accounts := testutil.CreateKeyringAccounts(t, kr, 2) + appAccount := accounts[0] + gatewayAccount := accounts[1] + + // Update the context with the new keyring + ctx = ctx.WithKeyring(kr) + + // Common args used for all requests + commonArgs := []string{ + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(net.Config.BondDenom, sdkmath.NewInt(10))).String()), + } + + tests := []struct { + desc string + appAddress string + gatewayAddress string + err *sdkerrors.Error + }{ + { + desc: "delegate to gateway: valid", + appAddress: appAccount.Address.String(), + gatewayAddress: gatewayAccount.Address.String(), + }, + { + desc: "invalid - missing app address", + // appAddress: appAccount.Address.String(), + gatewayAddress: gatewayAccount.Address.String(), + err: types.ErrAppInvalidAddress, + }, + { + desc: "invalid - invalid app address", + appAddress: "invalid address", + gatewayAddress: gatewayAccount.Address.String(), + err: types.ErrAppInvalidAddress, + }, + { + desc: "invalid - missing gateway address", + appAddress: appAccount.Address.String(), + // gatewayAddress: gatewayAccount.Address.String(), + err: types.ErrAppInvalidGatewayAddress, + }, + { + desc: "invalid - invalid gateway address", + appAddress: appAccount.Address.String(), + gatewayAddress: "invalid address", + err: types.ErrAppInvalidGatewayAddress, + }, + } + + // Initialize the App and Gateway Accounts by sending it some funds from the validator account that is part of genesis + network.InitAccount(t, net, appAccount.Address) + network.InitAccount(t, net, gatewayAccount.Address) + + // Run the tests + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + // Wait for a new block to be committed + require.NoError(t, net.WaitForNextBlock()) + + // Prepare the arguments for the CLI command + args := []string{ + tt.gatewayAddress, + fmt.Sprintf("--%s=%s", flags.FlagFrom, tt.appAddress), + } + args = append(args, commonArgs...) + + // Execute the command + delegateOutput, err := clitestutil.ExecTestCLICmd(ctx, cli.CmdDelegateToGateway(), args) + + // Validate the error if one is expected + if tt.err != nil { + stat, ok := status.FromError(tt.err) + require.True(t, ok) + require.Contains(t, stat.Message(), tt.err.Error()) + return + } + require.NoError(t, err) + + // Check the response + var resp sdk.TxResponse + require.NoError(t, net.Config.Codec.UnmarshalJSON(delegateOutput.Bytes(), &resp)) + require.NotNil(t, resp) + require.NotNil(t, resp.TxHash) + require.Equal(t, uint32(0), resp.Code) + }) + } +} diff --git a/x/application/keeper/keeper.go b/x/application/keeper/keeper.go index d115dddd5..6d0f54b47 100644 --- a/x/application/keeper/keeper.go +++ b/x/application/keeper/keeper.go @@ -19,7 +19,9 @@ type ( memKey storetypes.StoreKey paramstore paramtypes.Subspace - bankKeeper types.BankKeeper + bankKeeper types.BankKeeper + accountKeeper types.AccountKeeper + gatewayKeeper types.GatewayKeeper } ) @@ -30,6 +32,8 @@ func NewKeeper( ps paramtypes.Subspace, bankKeeper types.BankKeeper, + accountKeeper types.AccountKeeper, + gatewayKeeper types.GatewayKeeper, ) *Keeper { // set KeyTable if it has not already been set if !ps.HasKeyTable() { @@ -42,7 +46,9 @@ func NewKeeper( memKey: memKey, paramstore: ps, - bankKeeper: bankKeeper, + bankKeeper: bankKeeper, + accountKeeper: accountKeeper, + gatewayKeeper: gatewayKeeper, } } diff --git a/x/application/keeper/msg_server_delegate_to_gateway.go b/x/application/keeper/msg_server_delegate_to_gateway.go index 732bd5a4e..41b96e1ef 100644 --- a/x/application/keeper/msg_server_delegate_to_gateway.go +++ b/x/application/keeper/msg_server_delegate_to_gateway.go @@ -3,20 +3,52 @@ package keeper import ( "context" - sdk "github.com/cosmos/cosmos-sdk/types" - "pocket/x/application/types" + + sdkerrors "cosmossdk.io/errors" + sdk "github.com/cosmos/cosmos-sdk/types" ) func (k msgServer) DelegateToGateway(goCtx context.Context, msg *types.MsgDelegateToGateway) (*types.MsgDelegateToGatewayResponse, error) { ctx := sdk.UnwrapSDKContext(goCtx) + logger := k.Logger(ctx).With("method", "DelegateToGateway") + logger.Info("About to delegate application to gateway with msg: %v", msg) + if err := msg.ValidateBasic(); err != nil { + logger.Error("Delegation Message failed basic validation: %v", err) return nil, err } - // TODO: Handling the message - _ = ctx + // Retrieve the application from the store + app, found := k.GetApplication(ctx, msg.AppAddress) + if !found { + logger.Info("Application not found with address [%s]", msg.AppAddress) + return nil, sdkerrors.Wrapf(types.ErrAppNotFound, "application not found with address: %s", msg.AppAddress) + } + logger.Info("Application found with address [%s]", msg.AppAddress) + + // Check if the gateway is staked + if _, found := k.gatewayKeeper.GetGateway(ctx, msg.GatewayAddress); !found { + logger.Info("Gateway not found with address [%s]", msg.GatewayAddress) + return nil, sdkerrors.Wrapf(types.ErrAppGatewayNotFound, "gateway not found with address: %s", msg.GatewayAddress) + } + + // Check if the application is already delegated to the gateway + for _, gatewayAddr := range app.DelegateeGatewayAddresses { + if gatewayAddr == msg.GatewayAddress { + logger.Info("Application already delegated to gateway with address [%s]", msg.GatewayAddress) + return nil, sdkerrors.Wrapf(types.ErrAppAlreadyDelegated, "application already delegated to gateway with address: %s", msg.GatewayAddress) + } + } + + // Update the application with the new delegatee public key + app.DelegateeGatewayAddresses = append(app.DelegateeGatewayAddresses, msg.GatewayAddress) + logger.Info("Successfully added delegatee public key to application") + + // Update the application store with the new delegation + k.SetApplication(ctx, app) + logger.Info("Successfully delegated application to gateway for app: %+v", app) return &types.MsgDelegateToGatewayResponse{}, nil } diff --git a/x/application/keeper/msg_server_delegate_to_gateway_test.go b/x/application/keeper/msg_server_delegate_to_gateway_test.go new file mode 100644 index 000000000..788b740b8 --- /dev/null +++ b/x/application/keeper/msg_server_delegate_to_gateway_test.go @@ -0,0 +1,184 @@ +package keeper_test + +import ( + "testing" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/require" + + keepertest "pocket/testutil/keeper" + "pocket/testutil/sample" + "pocket/x/application/keeper" + "pocket/x/application/types" + sharedtypes "pocket/x/shared/types" +) + +func TestMsgServer_DelegateToGateway_SuccessfullyDelegate(t *testing.T) { + k, ctx := keepertest.ApplicationKeeper(t) + srv := keeper.NewMsgServerImpl(*k) + wctx := sdk.WrapSDKContext(ctx) + + // Generate an address for the application and gateways + appAddr := sample.AccAddress() + gatewayAddr1 := sample.AccAddress() + gatewayAddr2 := sample.AccAddress() + // Mock the gateway being staked via the staked gateway map + keepertest.StakedGatewayMap[gatewayAddr1] = struct{}{} + keepertest.StakedGatewayMap[gatewayAddr2] = struct{}{} + t.Cleanup(func() { + delete(keepertest.StakedGatewayMap, gatewayAddr1) + delete(keepertest.StakedGatewayMap, gatewayAddr2) + }) + + // Prepare the application + stakeMsg := &types.MsgStakeApplication{ + Address: appAddr, + Stake: &sdk.Coin{Denom: "upokt", Amount: sdk.NewInt(100)}, + Services: []*sharedtypes.ApplicationServiceConfig{ + { + ServiceId: &sharedtypes.ServiceId{Id: "svc1"}, + }, + }, + } + + // Stake the application & verify that the application exists + _, err := srv.StakeApplication(wctx, stakeMsg) + require.NoError(t, err) + _, isAppFound := k.GetApplication(ctx, appAddr) + require.True(t, isAppFound) + + // Prepare the delegation message + delegateMsg := &types.MsgDelegateToGateway{ + AppAddress: appAddr, + GatewayAddress: gatewayAddr1, + } + + // Delegate the application to the gateway + _, err = srv.DelegateToGateway(wctx, delegateMsg) + require.NoError(t, err) + + // Verify that the application exists + foundApp, isAppFound := k.GetApplication(ctx, appAddr) + require.True(t, isAppFound) + require.Equal(t, appAddr, foundApp.Address) + require.Equal(t, 1, len(foundApp.DelegateeGatewayAddresses)) + require.Equal(t, gatewayAddr1, foundApp.DelegateeGatewayAddresses[0]) + + // Prepare a second delegation message + delegateMsg2 := &types.MsgDelegateToGateway{ + AppAddress: appAddr, + GatewayAddress: gatewayAddr2, + } + + // Delegate the application to the second gateway + _, err = srv.DelegateToGateway(wctx, delegateMsg2) + require.NoError(t, err) + foundApp, isAppFound = k.GetApplication(ctx, appAddr) + require.True(t, isAppFound) + require.Equal(t, 2, len(foundApp.DelegateeGatewayAddresses)) + require.Equal(t, gatewayAddr1, foundApp.DelegateeGatewayAddresses[0]) + require.Equal(t, gatewayAddr2, foundApp.DelegateeGatewayAddresses[1]) +} + +func TestMsgServer_DelegateToGateway_FailDuplicate(t *testing.T) { + k, ctx := keepertest.ApplicationKeeper(t) + srv := keeper.NewMsgServerImpl(*k) + wctx := sdk.WrapSDKContext(ctx) + + // Generate an address for the application and gateway + appAddr := sample.AccAddress() + gatewayAddr := sample.AccAddress() + // Mock the gateway being staked via the staked gateway map + keepertest.StakedGatewayMap[gatewayAddr] = struct{}{} + t.Cleanup(func() { + delete(keepertest.StakedGatewayMap, gatewayAddr) + }) + + // Prepare the application + stakeMsg := &types.MsgStakeApplication{ + Address: appAddr, + Stake: &sdk.Coin{Denom: "upokt", Amount: sdk.NewInt(100)}, + Services: []*sharedtypes.ApplicationServiceConfig{ + { + ServiceId: &sharedtypes.ServiceId{Id: "svc1"}, + }, + }, + } + + // Stake the application & verify that the application exists + _, err := srv.StakeApplication(wctx, stakeMsg) + require.NoError(t, err) + _, isAppFound := k.GetApplication(ctx, appAddr) + require.True(t, isAppFound) + + // Prepare the delegation message + delegateMsg := &types.MsgDelegateToGateway{ + AppAddress: appAddr, + GatewayAddress: gatewayAddr, + } + + // Delegate the application to the gateway + _, err = srv.DelegateToGateway(wctx, delegateMsg) + require.NoError(t, err) + + // Verify that the application exists + foundApp, isAppFound := k.GetApplication(ctx, appAddr) + require.True(t, isAppFound) + require.Equal(t, appAddr, foundApp.Address) + require.Equal(t, 1, len(foundApp.DelegateeGatewayAddresses)) + require.Equal(t, gatewayAddr, foundApp.DelegateeGatewayAddresses[0]) + + // Prepare a second delegation message + delegateMsg2 := &types.MsgDelegateToGateway{ + AppAddress: appAddr, + GatewayAddress: gatewayAddr, + } + + // Attempt to delegate the application to the gateway again + _, err = srv.DelegateToGateway(wctx, delegateMsg2) + require.Error(t, err) + foundApp, isAppFound = k.GetApplication(ctx, appAddr) + require.True(t, isAppFound) + require.Equal(t, 1, len(foundApp.DelegateeGatewayAddresses)) + require.Equal(t, gatewayAddr, foundApp.DelegateeGatewayAddresses[0]) +} + +func TestMsgServer_DelegateToGateway_FailGatewayNotStaked(t *testing.T) { + k, ctx := keepertest.ApplicationKeeper(t) + srv := keeper.NewMsgServerImpl(*k) + wctx := sdk.WrapSDKContext(ctx) + + // Generate an address for the application and gateway + appAddr := sample.AccAddress() + gatewayAddr := sample.AccAddress() + + // Prepare the application + stakeMsg := &types.MsgStakeApplication{ + Address: appAddr, + Stake: &sdk.Coin{Denom: "upokt", Amount: sdk.NewInt(100)}, + Services: []*sharedtypes.ApplicationServiceConfig{ + { + ServiceId: &sharedtypes.ServiceId{Id: "svc1"}, + }, + }, + } + + // Stake the application & verify that the application exists + _, err := srv.StakeApplication(wctx, stakeMsg) + require.NoError(t, err) + _, isAppFound := k.GetApplication(ctx, appAddr) + require.True(t, isAppFound) + + // Prepare the delegation message + delegateMsg := &types.MsgDelegateToGateway{ + AppAddress: appAddr, + GatewayAddress: gatewayAddr, + } + + // Attempt to delegate the application to the unstaked gateway + _, err = srv.DelegateToGateway(wctx, delegateMsg) + require.Error(t, err) + foundApp, isAppFound := k.GetApplication(ctx, appAddr) + require.True(t, isAppFound) + require.Equal(t, 0, len(foundApp.DelegateeGatewayAddresses)) +} diff --git a/x/application/keeper/msg_server_stake_application.go b/x/application/keeper/msg_server_stake_application.go index 6aa824e24..1d6fbcaf1 100644 --- a/x/application/keeper/msg_server_stake_application.go +++ b/x/application/keeper/msg_server_stake_application.go @@ -67,9 +67,10 @@ func (k msgServer) createApplication( msg *types.MsgStakeApplication, ) types.Application { return types.Application{ - Address: msg.Address, - Stake: msg.Stake, - ServiceConfigs: msg.Services, + Address: msg.Address, + Stake: msg.Stake, + ServiceConfigs: msg.Services, + DelegateeGatewayAddresses: make([]string, 0), } } diff --git a/x/application/simulation/delegate_to_gateway.go b/x/application/simulation/delegate_to_gateway.go index 35ea46bee..b9e337d58 100644 --- a/x/application/simulation/delegate_to_gateway.go +++ b/x/application/simulation/delegate_to_gateway.go @@ -3,11 +3,12 @@ package simulation import ( "math/rand" + "pocket/x/application/keeper" + "pocket/x/application/types" + "github.com/cosmos/cosmos-sdk/baseapp" sdk "github.com/cosmos/cosmos-sdk/types" simtypes "github.com/cosmos/cosmos-sdk/types/simulation" - "pocket/x/application/keeper" - "pocket/x/application/types" ) func SimulateMsgDelegateToGateway( @@ -17,9 +18,11 @@ func SimulateMsgDelegateToGateway( ) simtypes.Operation { return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simtypes.Account, chainID string, ) (simtypes.OperationMsg, []simtypes.FutureOperation, error) { - simAccount, _ := simtypes.RandomAcc(r, accs) + simAppAccount, _ := simtypes.RandomAcc(r, accs) + simGatewayAccount, _ := simtypes.RandomAcc(r, accs) msg := &types.MsgDelegateToGateway{ - Address: simAccount.Address.String(), + AppAddress: simAppAccount.Address.String(), + GatewayAddress: simGatewayAccount.Address.String(), } // TODO: Handling the DelegateToGateway simulation diff --git a/x/application/types/errors.go b/x/application/types/errors.go index 0a84948da..7ed7fc8e2 100644 --- a/x/application/types/errors.go +++ b/x/application/types/errors.go @@ -12,5 +12,8 @@ var ( ErrAppInvalidAddress = sdkerrors.Register(ModuleName, 2, "invalid application address") ErrAppUnauthorized = sdkerrors.Register(ModuleName, 3, "unauthorized application signer") ErrAppNotFound = sdkerrors.Register(ModuleName, 4, "application not found") - ErrAppInvalidServiceConfigs = sdkerrors.Register(ModuleName, 5, "invalid service configs") + ErrAppInvalidServiceConfigs = sdkerrors.Register(ModuleName, 6, "invalid service configs") + ErrAppGatewayNotFound = sdkerrors.Register(ModuleName, 7, "gateway not found") + ErrAppInvalidGatewayAddress = sdkerrors.Register(ModuleName, 8, "invalid gateway address") + ErrAppAlreadyDelegated = sdkerrors.Register(ModuleName, 9, "application already delegated to gateway") ) diff --git a/x/application/types/expected_keepers.go b/x/application/types/expected_keepers.go index ff977bf18..ab3cd615d 100644 --- a/x/application/types/expected_keepers.go +++ b/x/application/types/expected_keepers.go @@ -1,10 +1,12 @@ package types -//go:generate mockgen -destination ../../../testutil/application/mocks/expected_keepers_mock.go -package mocks . AccountKeeper,BankKeeper +//go:generate mockgen -destination ../../../testutil/application/mocks/expected_keepers_mock.go -package mocks . AccountKeeper,BankKeeper,GatewayKeeper import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/auth/types" + + gatewaytypes "pocket/x/gateway/types" ) // AccountKeeper defines the expected account keeper used for simulations (noalias) @@ -17,3 +19,8 @@ type BankKeeper interface { DelegateCoinsFromAccountToModule(ctx sdk.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) error UndelegateCoinsFromModuleToAccount(ctx sdk.Context, senderModule string, recipientAddr sdk.AccAddress, amt sdk.Coins) error } + +// GatewayKeeper defines the expected interface needed to retrieve gateway information. +type GatewayKeeper interface { + GetGateway(ctx sdk.Context, addr string) (gatewaytypes.Gateway, bool) +} diff --git a/x/application/types/genesis.go b/x/application/types/genesis.go index 1d8d5f892..38d2b25dd 100644 --- a/x/application/types/genesis.go +++ b/x/application/types/genesis.go @@ -34,7 +34,7 @@ func (gs GenesisState) Validate() error { applicationIndexMap[index] = struct{}{} } - // Check that the stake value for the apps is valid + // Check that the stake value for the apps is valid and that the delegatee pubkeys are valid for _, app := range gs.ApplicationList { // TODO_TECHDEBT: Consider creating shared helpers across the board for stake validation, // similar to how we have `AreValidAppServiceConfigs` below @@ -55,7 +55,13 @@ func (gs GenesisState) Validate() error { return sdkerrors.Wrapf(ErrAppInvalidStake, "invalid stake amount denom for application %v", app.Stake) } - // Valid the application service configs + // Check that the application's delegated gateway addresses are valid + for _, gatewayAddr := range app.DelegateeGatewayAddresses { + if _, err := sdk.AccAddressFromBech32(gatewayAddr); err != nil { + return sdkerrors.Wrapf(ErrAppInvalidGatewayAddress, "invalid gateway address %s; (%v)", gatewayAddr, err) + } + } + // Validate the application service configs if reason, ok := servicehelpers.AreValidAppServiceConfigs(app.ServiceConfigs); !ok { return sdkerrors.Wrapf(ErrAppInvalidStake, reason) diff --git a/x/application/types/genesis_test.go b/x/application/types/genesis_test.go index f1eed28b3..90790e842 100644 --- a/x/application/types/genesis_test.go +++ b/x/application/types/genesis_test.go @@ -24,6 +24,10 @@ func TestGenesisState_Validate(t *testing.T) { ServiceId: &sharedtypes.ServiceId{Id: "svc2"}, } + emptyDelegatees := make([]string, 0) + gatewayAddr1 := sample.AccAddress() + gatewayAddr2 := sample.AccAddress() + tests := []struct { desc string genState *types.GenesisState @@ -37,17 +41,18 @@ func TestGenesisState_Validate(t *testing.T) { { desc: "valid genesis state", genState: &types.GenesisState{ - ApplicationList: []types.Application{ { - Address: addr1, - Stake: &stake1, - ServiceConfigs: []*sharedtypes.ApplicationServiceConfig{svc1AppConfig}, + Address: addr1, + Stake: &stake1, + ServiceConfigs: []*sharedtypes.ApplicationServiceConfig{svc1AppConfig}, + DelegateeGatewayAddresses: []string{gatewayAddr1, gatewayAddr2}, }, { - Address: addr2, - Stake: &stake2, - ServiceConfigs: []*sharedtypes.ApplicationServiceConfig{svc2AppConfig}, + Address: addr2, + Stake: &stake2, + ServiceConfigs: []*sharedtypes.ApplicationServiceConfig{svc2AppConfig}, + DelegateeGatewayAddresses: []string{gatewayAddr2, gatewayAddr1}, }, }, // this line is used by starport scaffolding # types/genesis/validField @@ -59,14 +64,16 @@ func TestGenesisState_Validate(t *testing.T) { genState: &types.GenesisState{ ApplicationList: []types.Application{ { - Address: addr1, - Stake: &stake1, - ServiceConfigs: []*sharedtypes.ApplicationServiceConfig{svc1AppConfig}, + Address: addr1, + Stake: &stake1, + ServiceConfigs: []*sharedtypes.ApplicationServiceConfig{svc1AppConfig}, + DelegateeGatewayAddresses: emptyDelegatees, }, { - Address: addr2, - Stake: &sdk.Coin{Denom: "upokt", Amount: sdk.NewInt(0)}, - ServiceConfigs: []*sharedtypes.ApplicationServiceConfig{svc2AppConfig}, + Address: addr2, + Stake: &sdk.Coin{Denom: "upokt", Amount: sdk.NewInt(0)}, + ServiceConfigs: []*sharedtypes.ApplicationServiceConfig{svc2AppConfig}, + DelegateeGatewayAddresses: emptyDelegatees, }, }, }, @@ -77,14 +84,16 @@ func TestGenesisState_Validate(t *testing.T) { genState: &types.GenesisState{ ApplicationList: []types.Application{ { - Address: addr1, - Stake: &stake1, - ServiceConfigs: []*sharedtypes.ApplicationServiceConfig{svc1AppConfig}, + Address: addr1, + Stake: &stake1, + ServiceConfigs: []*sharedtypes.ApplicationServiceConfig{svc1AppConfig}, + DelegateeGatewayAddresses: emptyDelegatees, }, { - Address: addr2, - Stake: &sdk.Coin{Denom: "upokt", Amount: sdk.NewInt(-100)}, - ServiceConfigs: []*sharedtypes.ApplicationServiceConfig{svc2AppConfig}, + Address: addr2, + Stake: &sdk.Coin{Denom: "upokt", Amount: sdk.NewInt(-100)}, + ServiceConfigs: []*sharedtypes.ApplicationServiceConfig{svc2AppConfig}, + DelegateeGatewayAddresses: emptyDelegatees, }, }, }, @@ -95,14 +104,16 @@ func TestGenesisState_Validate(t *testing.T) { genState: &types.GenesisState{ ApplicationList: []types.Application{ { - Address: addr1, - Stake: &stake1, - ServiceConfigs: []*sharedtypes.ApplicationServiceConfig{svc1AppConfig}, + Address: addr1, + Stake: &stake1, + ServiceConfigs: []*sharedtypes.ApplicationServiceConfig{svc1AppConfig}, + DelegateeGatewayAddresses: emptyDelegatees, }, { - Address: addr2, - Stake: &sdk.Coin{Denom: "invalid", Amount: sdk.NewInt(100)}, - ServiceConfigs: []*sharedtypes.ApplicationServiceConfig{svc2AppConfig}, + Address: addr2, + Stake: &sdk.Coin{Denom: "invalid", Amount: sdk.NewInt(100)}, + ServiceConfigs: []*sharedtypes.ApplicationServiceConfig{svc2AppConfig}, + DelegateeGatewayAddresses: emptyDelegatees, }, }, }, @@ -113,14 +124,16 @@ func TestGenesisState_Validate(t *testing.T) { genState: &types.GenesisState{ ApplicationList: []types.Application{ { - Address: addr1, - Stake: &stake1, - ServiceConfigs: []*sharedtypes.ApplicationServiceConfig{svc1AppConfig}, + Address: addr1, + Stake: &stake1, + ServiceConfigs: []*sharedtypes.ApplicationServiceConfig{svc1AppConfig}, + DelegateeGatewayAddresses: emptyDelegatees, }, { - Address: addr2, - Stake: &sdk.Coin{Denom: "", Amount: sdk.NewInt(100)}, - ServiceConfigs: []*sharedtypes.ApplicationServiceConfig{svc2AppConfig}, + Address: addr2, + Stake: &sdk.Coin{Denom: "", Amount: sdk.NewInt(100)}, + ServiceConfigs: []*sharedtypes.ApplicationServiceConfig{svc2AppConfig}, + DelegateeGatewayAddresses: emptyDelegatees, }, }, }, @@ -131,14 +144,16 @@ func TestGenesisState_Validate(t *testing.T) { genState: &types.GenesisState{ ApplicationList: []types.Application{ { - Address: addr1, - Stake: &stake1, - ServiceConfigs: []*sharedtypes.ApplicationServiceConfig{svc1AppConfig}, + Address: addr1, + Stake: &stake1, + ServiceConfigs: []*sharedtypes.ApplicationServiceConfig{svc1AppConfig}, + DelegateeGatewayAddresses: emptyDelegatees, }, { - Address: addr1, - Stake: &stake2, - ServiceConfigs: []*sharedtypes.ApplicationServiceConfig{svc2AppConfig}, + Address: addr1, + Stake: &stake2, + ServiceConfigs: []*sharedtypes.ApplicationServiceConfig{svc2AppConfig}, + DelegateeGatewayAddresses: emptyDelegatees, }, }, }, @@ -149,14 +164,16 @@ func TestGenesisState_Validate(t *testing.T) { genState: &types.GenesisState{ ApplicationList: []types.Application{ { - Address: addr1, - Stake: &stake1, - ServiceConfigs: []*sharedtypes.ApplicationServiceConfig{svc1AppConfig}, + Address: addr1, + Stake: &stake1, + ServiceConfigs: []*sharedtypes.ApplicationServiceConfig{svc1AppConfig}, + DelegateeGatewayAddresses: emptyDelegatees, }, { - Address: addr2, - Stake: nil, - ServiceConfigs: []*sharedtypes.ApplicationServiceConfig{svc2AppConfig}, + Address: addr2, + Stake: nil, + ServiceConfigs: []*sharedtypes.ApplicationServiceConfig{svc2AppConfig}, + DelegateeGatewayAddresses: emptyDelegatees, }, }, }, @@ -167,14 +184,56 @@ func TestGenesisState_Validate(t *testing.T) { genState: &types.GenesisState{ ApplicationList: []types.Application{ { - Address: addr1, - Stake: &stake1, - ServiceConfigs: []*sharedtypes.ApplicationServiceConfig{svc1AppConfig}, + Address: addr1, + Stake: &stake1, + ServiceConfigs: []*sharedtypes.ApplicationServiceConfig{svc1AppConfig}, + DelegateeGatewayAddresses: emptyDelegatees, }, { Address: addr2, // Explicitly missing stake - ServiceConfigs: []*sharedtypes.ApplicationServiceConfig{svc2AppConfig}, + ServiceConfigs: []*sharedtypes.ApplicationServiceConfig{svc2AppConfig}, + DelegateeGatewayAddresses: emptyDelegatees, + }, + }, + }, + valid: false, + }, + { + desc: "invalid - due to invalid delegatee pub key", + genState: &types.GenesisState{ + ApplicationList: []types.Application{ + { + Address: addr1, + Stake: &stake1, + ServiceConfigs: []*sharedtypes.ApplicationServiceConfig{svc1AppConfig}, + DelegateeGatewayAddresses: emptyDelegatees, + }, + { + Address: addr2, + Stake: &stake2, + ServiceConfigs: []*sharedtypes.ApplicationServiceConfig{svc2AppConfig}, + DelegateeGatewayAddresses: []string{"invalid address"}, + }, + }, + }, + valid: false, + }, + { + desc: "invalid - due to invalid delegatee pub keys", + genState: &types.GenesisState{ + ApplicationList: []types.Application{ + { + Address: addr1, + Stake: &stake1, + ServiceConfigs: []*sharedtypes.ApplicationServiceConfig{svc1AppConfig}, + DelegateeGatewayAddresses: []string{gatewayAddr1}, + }, + { + Address: addr2, + Stake: &stake2, + ServiceConfigs: []*sharedtypes.ApplicationServiceConfig{svc2AppConfig}, + DelegateeGatewayAddresses: []string{"invalid address", gatewayAddr2}, }, }, }, @@ -188,6 +247,7 @@ func TestGenesisState_Validate(t *testing.T) { Address: addr1, Stake: &stake1, // ServiceConfigs: omitted + DelegateeGatewayAddresses: emptyDelegatees, }, }, }, @@ -198,9 +258,10 @@ func TestGenesisState_Validate(t *testing.T) { genState: &types.GenesisState{ ApplicationList: []types.Application{ { - Address: addr1, - Stake: &stake1, - ServiceConfigs: []*sharedtypes.ApplicationServiceConfig{}, + Address: addr1, + Stake: &stake1, + ServiceConfigs: []*sharedtypes.ApplicationServiceConfig{}, + DelegateeGatewayAddresses: emptyDelegatees, }, }, }, @@ -212,10 +273,11 @@ func TestGenesisState_Validate(t *testing.T) { ApplicationList: []types.Application{ { Address: addr1, - Stake: &stake1, + Stake: &stake1, ServiceConfigs: []*sharedtypes.ApplicationServiceConfig{ {ServiceId: &sharedtypes.ServiceId{Id: "12345678901"}}, }, + DelegateeGatewayAddresses: emptyDelegatees, }, }, }, @@ -234,6 +296,7 @@ func TestGenesisState_Validate(t *testing.T) { Name: "abcdefghijklmnopqrstuvwxyzab-abcdefghijklmnopqrstuvwxyzab", }}, }, + DelegateeGatewayAddresses: emptyDelegatees, }, }, }, @@ -249,6 +312,7 @@ func TestGenesisState_Validate(t *testing.T) { ServiceConfigs: []*sharedtypes.ApplicationServiceConfig{ {ServiceId: &sharedtypes.ServiceId{Id: "12 45 !"}}, }, + DelegateeGatewayAddresses: emptyDelegatees, }, }, }, diff --git a/x/application/types/message_delegate_to_gateway.go b/x/application/types/message_delegate_to_gateway.go index 232564310..652b5baa6 100644 --- a/x/application/types/message_delegate_to_gateway.go +++ b/x/application/types/message_delegate_to_gateway.go @@ -1,17 +1,18 @@ package types import ( + sdkerrors "cosmossdk.io/errors" sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" ) const TypeMsgDelegateToGateway = "delegate_to_gateway" var _ sdk.Msg = (*MsgDelegateToGateway)(nil) -func NewMsgDelegateToGateway(address string) *MsgDelegateToGateway { +func NewMsgDelegateToGateway(appAddress, gatewayAddress string) *MsgDelegateToGateway { return &MsgDelegateToGateway{ - Address: address, + AppAddress: appAddress, + GatewayAddress: gatewayAddress, } } @@ -24,7 +25,7 @@ func (msg *MsgDelegateToGateway) Type() string { } func (msg *MsgDelegateToGateway) GetSigners() []sdk.AccAddress { - address, err := sdk.AccAddressFromBech32(msg.Address) + address, err := sdk.AccAddressFromBech32(msg.AppAddress) if err != nil { panic(err) } @@ -37,9 +38,13 @@ func (msg *MsgDelegateToGateway) GetSignBytes() []byte { } func (msg *MsgDelegateToGateway) ValidateBasic() error { - _, err := sdk.AccAddressFromBech32(msg.Address) - if err != nil { - return sdkerrors.Wrapf(sdkerrors.ErrInvalidAddress, "invalid address address (%s)", err) + // Validate the application address + if _, err := sdk.AccAddressFromBech32(msg.AppAddress); err != nil { + return sdkerrors.Wrapf(ErrAppInvalidAddress, "invalid application address %s; (%v)", msg.AppAddress, err) + } + // Validate the gateway address + if _, err := sdk.AccAddressFromBech32(msg.GatewayAddress); err != nil { + return sdkerrors.Wrapf(ErrAppInvalidGatewayAddress, "invalid gateway address %s; (%v)", msg.GatewayAddress, err) } return nil } diff --git a/x/application/types/message_delegate_to_gateway_test.go b/x/application/types/message_delegate_to_gateway_test.go index 770d801b2..bf1254a29 100644 --- a/x/application/types/message_delegate_to_gateway_test.go +++ b/x/application/types/message_delegate_to_gateway_test.go @@ -3,9 +3,9 @@ package types import ( "testing" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - "github.com/stretchr/testify/require" "pocket/testutil/sample" + + "github.com/stretchr/testify/require" ) func TestMsgDelegateToGateway_ValidateBasic(t *testing.T) { @@ -15,15 +15,31 @@ func TestMsgDelegateToGateway_ValidateBasic(t *testing.T) { err error }{ { - name: "invalid address", + name: "invalid app address - no gateway address", + msg: MsgDelegateToGateway{ + AppAddress: "invalid_address", + // GatewayAddress: intentionally omitted, + }, + err: ErrAppInvalidAddress, + }, { + name: "valid app address - no gateway address", + msg: MsgDelegateToGateway{ + AppAddress: sample.AccAddress(), + // GatewayAddress: intentionally omitted, + }, + err: ErrAppInvalidGatewayAddress, + }, { + name: "valid app address - invalid gateway address", msg: MsgDelegateToGateway{ - Address: "invalid_address", + AppAddress: sample.AccAddress(), + GatewayAddress: "invalid_address", }, - err: sdkerrors.ErrInvalidAddress, + err: ErrAppInvalidGatewayAddress, }, { name: "valid address", msg: MsgDelegateToGateway{ - Address: sample.AccAddress(), + AppAddress: sample.AccAddress(), + GatewayAddress: sample.AccAddress(), }, }, } diff --git a/x/gateway/keeper/keeper.go b/x/gateway/keeper/keeper.go index c132d3a48..ea74f170b 100644 --- a/x/gateway/keeper/keeper.go +++ b/x/gateway/keeper/keeper.go @@ -19,8 +19,7 @@ type ( memKey storetypes.StoreKey paramstore paramtypes.Subspace - bankKeeper types.BankKeeper - accountKeeper types.AccountKeeper + bankKeeper types.BankKeeper } ) @@ -31,7 +30,6 @@ func NewKeeper( ps paramtypes.Subspace, bankKeeper types.BankKeeper, - accountKeeper types.AccountKeeper, ) *Keeper { // set KeyTable if it has not already been set if !ps.HasKeyTable() { @@ -44,8 +42,7 @@ func NewKeeper( memKey: memKey, paramstore: ps, - bankKeeper: bankKeeper, - accountKeeper: accountKeeper, + bankKeeper: bankKeeper, } }