Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: scalable version queries #384

Merged
merged 14 commits into from
Sep 13, 2021
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 65 additions & 0 deletions docs/ibc/proto-docs.md
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,12 @@

- [Msg](#ibc.core.connection.v1.Msg)

- [ibc/core/port/v1/query.proto](#ibc/core/port/v1/query.proto)
- [QueryPortRequest](#ibc.core.port.v1.QueryPortRequest)
- [QueryPortResponse](#ibc.core.port.v1.QueryPortResponse)

- [Query](#ibc.core.port.v1.Query)

- [ibc/core/types/v1/genesis.proto](#ibc/core/types/v1/genesis.proto)
- [GenesisState](#ibc.core.types.v1.GenesisState)

Expand Down Expand Up @@ -2874,6 +2880,65 @@ Msg defines the ibc/connection Msg service.



<a name="ibc/core/port/v1/query.proto"></a>
<p align="right"><a href="#top">Top</a></p>

## ibc/core/port/v1/query.proto



<a name="ibc.core.port.v1.QueryPortRequest"></a>

### QueryPortRequest
QueryPortRequest is the request type for the Query/Port RPC method


| Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- |
| `port_id` | [string](#string) | | port unique identifier |
| `counterparty` | [ibc.core.channel.v1.Counterparty](#ibc.core.channel.v1.Counterparty) | | counterparty channel end |
| `counterparty_version` | [string](#string) | | counterparty version |






<a name="ibc.core.port.v1.QueryPortResponse"></a>

### QueryPortResponse
QueryPortResponse is the response type for the Query/Port RPC method.


| Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- |
| `port_id` | [string](#string) | | port id associated with the request identifiers |
| `version` | [string](#string) | | supported app version |





<!-- end messages -->

<!-- end enums -->

<!-- end HasExtensions -->


<a name="ibc.core.port.v1.Query"></a>

### Query
Query provides defines the gRPC querier service

| Method Name | Request Type | Response Type | Description | HTTP Verb | Endpoint |
| ----------- | ------------ | ------------- | ------------| ------- | -------- |
| `Port` | [QueryPortRequest](#ibc.core.port.v1.QueryPortRequest) | [QueryPortResponse](#ibc.core.port.v1.QueryPortResponse) | Port queries an IBC Port. | |

<!-- end services -->



<a name="ibc/core/types/v1/genesis.proto"></a>
<p align="right"><a href="#top">Top</a></p>

Expand Down
14 changes: 14 additions & 0 deletions modules/apps/transfer/module.go
Original file line number Diff line number Diff line change
Expand Up @@ -434,3 +434,17 @@ func (am AppModule) OnTimeoutPacket(

return nil
}

// NegotiateAppVersion implements the IBCModule interface
func (am AppModule) NegotiateAppVersion(
ctx sdk.Context,
portID string,
counterparty channeltypes.Counterparty,
counterpartyVersion string,
) (version string, err error) {
if counterpartyVersion != types.Version {
return "", sdkerrors.Wrapf(types.ErrInvalidVersion, "invalid counterparty version: got: %s, expected %s", counterpartyVersion, types.Version)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can safely return an error here if there is a version mismatch.
One would imagine that a hypothetical "ics20-2" would involve breaking changes

}

return types.Version, nil
}
25 changes: 25 additions & 0 deletions modules/core/05-port/client/cli/cli.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package cli

import (
"github.com/spf13/cobra"

"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/ibc-go/modules/core/05-port/types"
)

// GetQueryCmd returns the query commands for IBC channels
colin-axner marked this conversation as resolved.
Show resolved Hide resolved
func GetQueryCmd() *cobra.Command {
queryCmd := &cobra.Command{
Use: types.SubModuleName,
Short: "IBC port query subcommands",
DisableFlagParsing: true,
SuggestionsMinimumDistance: 2,
RunE: client.ValidateCmd,
}

queryCmd.AddCommand(
GetCmdQueryPort(),
)

return queryCmd
}
54 changes: 54 additions & 0 deletions modules/core/05-port/client/cli/query.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package cli

import (
"fmt"

"github.com/spf13/cobra"

"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/flags"
"github.com/cosmos/cosmos-sdk/version"

channeltypes "github.com/cosmos/ibc-go/modules/core/04-channel/types"
"github.com/cosmos/ibc-go/modules/core/05-port/types"
host "github.com/cosmos/ibc-go/modules/core/24-host"
)

// GetCmdQueryPorts defines the command to query a port
colin-axner marked this conversation as resolved.
Show resolved Hide resolved
func GetCmdQueryPort() *cobra.Command {
cmd := &cobra.Command{
Use: "port [port-id] [counterparty-port-id] [counterparty-channel-id] [counterparty-version]",
Short: "Query an IBC port",
Long: "Query an IBC port by providing it's port ID and associated counterparty port and channel identifiers",
Example: fmt.Sprintf("%s query %s %s port", version.AppName, host.ModuleName, types.SubModuleName),
Args: cobra.ExactArgs(4),
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx, err := client.GetClientQueryContext(cmd)
if err != nil {
return err
}

queryClient := types.NewQueryClient(clientCtx)

req := &types.QueryPortRequest{
PortId: args[0],
Counterparty: &channeltypes.Counterparty{
PortId: args[1],
ChannelId: args[2],
},
CounterpartyVersion: args[3],
}

portRes, err := queryClient.Port(cmd.Context(), req)
if err != nil {
return err
}

return clientCtx.PrintProto(portRes)
},
}

flags.AddQueryFlagsToCmd(cmd)

return cmd
}
52 changes: 52 additions & 0 deletions modules/core/05-port/keeper/grpc_query.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package keeper

import (
"context"

sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"

"github.com/cosmos/ibc-go/modules/core/05-port/types"
host "github.com/cosmos/ibc-go/modules/core/24-host"
)

var _ types.QueryServer = (*Keeper)(nil)

// Port implements the Query/Port gRPC method
func (q Keeper) Port(c context.Context, req *types.QueryPortRequest) (*types.QueryPortResponse, error) {
if req == nil {
return nil, status.Error(codes.InvalidArgument, "empty request")
}

if err := validategRPCRequest(req.PortId); err != nil {
return nil, err
}

ctx := sdk.UnwrapSDKContext(c)
module, _, err := q.LookupModuleByPort(ctx, req.PortId)
if err != nil {
return nil, status.Errorf(codes.NotFound, sdkerrors.Wrap(err, "could not retrieve module from port-id").Error())
}

ibcModule, found := q.Router.GetRoute(module)
if !found {
return nil, status.Errorf(codes.NotFound, sdkerrors.Wrapf(types.ErrInvalidRoute, "route not found to module: %s", module).Error())
}

version, err := ibcModule.NegotiateAppVersion(ctx, req.PortId, *req.Counterparty, req.CounterpartyVersion)
if err != nil {
return nil, status.Errorf(codes.InvalidArgument, sdkerrors.Wrap(err, "version negotation failed").Error())
}

return types.NewQueryPortResponse(req.PortId, version), nil
}

func validategRPCRequest(portID string) error {
if err := host.PortIdentifierValidator(portID); err != nil {
return status.Error(codes.InvalidArgument, err.Error())
}

return nil
}
2 changes: 2 additions & 0 deletions modules/core/05-port/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import (

// Keeper defines the IBC connection keeper
type Keeper struct {
Router *types.Router

scopedKeeper capabilitykeeper.ScopedKeeper
}

Expand Down
24 changes: 24 additions & 0 deletions modules/core/05-port/module.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package port

import (
"github.com/gogo/protobuf/grpc"
"github.com/spf13/cobra"

"github.com/cosmos/ibc-go/modules/core/05-port/types"
"github.com/cosmos/ibc-go/modules/core/client/cli"
)

// Name returns the IBC port ICS name.
func Name() string {
return types.SubModuleName
}

// GetQueryCmd returns the root query command for IBC ports.
func GetQueryCmd() *cobra.Command {
return cli.GetQueryCmd()
}

// RegisterQueryService registers the gRPC query service for IBC ports.
func RegisterQueryService(server grpc.Server, queryServer types.QueryServer) {
types.RegisterQueryServer(server, queryServer)
}
9 changes: 5 additions & 4 deletions modules/core/05-port/types/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@ import (

// IBC port sentinel errors
var (
ErrPortExists = sdkerrors.Register(SubModuleName, 2, "port is already binded")
ErrPortNotFound = sdkerrors.Register(SubModuleName, 3, "port not found")
ErrInvalidPort = sdkerrors.Register(SubModuleName, 4, "invalid port")
ErrInvalidRoute = sdkerrors.Register(SubModuleName, 5, "route not found")
ErrPortExists = sdkerrors.Register(SubModuleName, 2, "port is already binded")
ErrPortNotFound = sdkerrors.Register(SubModuleName, 3, "port not found")
ErrInvalidPort = sdkerrors.Register(SubModuleName, 4, "invalid port")
ErrInvalidRoute = sdkerrors.Register(SubModuleName, 5, "route not found")
ErrInvalidVersion = sdkerrors.Register(SubModuleName, 6, "invalid port version")
damiannolan marked this conversation as resolved.
Show resolved Hide resolved
)
7 changes: 7 additions & 0 deletions modules/core/05-port/types/module.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,4 +82,11 @@ type IBCModule interface {
packet channeltypes.Packet,
relayer sdk.AccAddress,
) error

NegotiateAppVersion(
colin-axner marked this conversation as resolved.
Show resolved Hide resolved
ctx sdk.Context,
portID string,
counterparty channeltypes.Counterparty,
counterpartyVersion string,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Information not provided:

  • channel id
  • channel ordering
  • channel state
  • connection hop (connection channel is attached to)

channel id cannot necessarily be known by OnChanOpenTry. Channel state shouldn't matter

Should we include channel ordering and connection hops? We could always break API later to add it? I have no strong opinion except knowing the associated connection may be useful

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think there is no harm in adding it now. If I'm understanding you correctly, no current module requires these parameters for negotiating the counterparty version but adding them now will let future modules use as required?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct. To the best of my knowledge, interchain accounts is the first module introducing multiple valid versions. It's hard to know what version negotiation will look like for applications, but I agree probably best to be as flexible as possible

) (version string, err error)
}
9 changes: 9 additions & 0 deletions modules/core/05-port/types/query.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package types

// NewQueryPortResponse creates a new QueryPortResponse instance
func NewQueryPortResponse(portID, version string) *QueryPortResponse {
return &QueryPortResponse{
PortId: portID,
Version: version,
}
}
Loading