Skip to content

Commit

Permalink
feat: scalable version queries (#384)
Browse files Browse the repository at this point in the history
* adding protos for port query interface

* adding NegotiateAppVersion method to IBCModule interface

* adding grpc port query implementation and module surrounds

* adding NegotiateAppVersion implementation to apps/transfer and mocks

* updating ErrInvalidVersion error code

* adding grpc query tests for 05-port

* updating grpc naming, adding godocs, removing redundant query cli

* updating grpc query tests

* adding changelog entry for #384 app version negotiation

* fixing error formatting in transfer version negotiation

Co-authored-by: colin axnér <25233464+colin-axner@users.noreply.github.com>

* removing client/cli query

* updating grpc query naming, adding new fields and associated surrounds

Co-authored-by: colin axnér <25233464+colin-axner@users.noreply.github.com>
  • Loading branch information
damiannolan and colin-axner authored Sep 13, 2021
1 parent 2fbe682 commit 85b4383
Show file tree
Hide file tree
Showing 16 changed files with 1,196 additions and 1 deletion.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ Ref: https://keepachangelog.com/en/1.0.0/

### Features
* [\#372](https://github.com/cosmos/ibc-go/pull/372) New CLI command `query ibc client status <client id>` to get the current activity status of a client
* [\#384](https://github.com/cosmos/ibc-go/pull/384) Added `NegotiateAppVersion` method to `IBCModule` interface supported by a gRPC query service in `05-port`. This provides routing of requests to the desired application module callback, which in turn performs application version negotiation.

## [v1.0.0](https://github.com/cosmos/ibc-go/releases/tag/v1.0.0) - 2021-08-10

Expand Down
67 changes: 67 additions & 0 deletions docs/ibc/proto-docs.md
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,12 @@

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

- [ibc/core/port/v1/query.proto](#ibc/core/port/v1/query.proto)
- [QueryAppVersionRequest](#ibc.core.port.v1.QueryAppVersionRequest)
- [QueryAppVersionResponse](#ibc.core.port.v1.QueryAppVersionResponse)

- [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 @@ -2892,6 +2898,67 @@ 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.QueryAppVersionRequest"></a>

### QueryAppVersionRequest
QueryAppVersionRequest is the request type for the Query/AppVersion RPC method


| Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- |
| `port_id` | [string](#string) | | port unique identifier |
| `connection_id` | [string](#string) | | connection unique identifier |
| `ordering` | [ibc.core.channel.v1.Order](#ibc.core.channel.v1.Order) | | whether the channel is ordered or unordered |
| `counterparty` | [ibc.core.channel.v1.Counterparty](#ibc.core.channel.v1.Counterparty) | | counterparty channel end |
| `proposed_version` | [string](#string) | | proposed version |






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

### QueryAppVersionResponse
QueryAppVersionResponse is the response type for the Query/AppVersion 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 defines the gRPC querier service

| Method Name | Request Type | Response Type | Description | HTTP Verb | Endpoint |
| ----------- | ------------ | ------------- | ------------| ------- | -------- |
| `AppVersion` | [QueryAppVersionRequest](#ibc.core.port.v1.QueryAppVersionRequest) | [QueryAppVersionResponse](#ibc.core.port.v1.QueryAppVersionResponse) | AppVersion queries an IBC Port and determines the appropriate application version to be used | |

<!-- end services -->



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

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

return nil
}

// NegotiateAppVersion implements the IBCModule interface
func (am AppModule) NegotiateAppVersion(
ctx sdk.Context,
order channeltypes.Order,
connectionID string,
portID string,
counterparty channeltypes.Counterparty,
proposedVersion string,
) (string, error) {
if proposedVersion != types.Version {
return "", sdkerrors.Wrapf(types.ErrInvalidVersion, "failed to negotiate app version: expected %s, got %s", types.Version, proposedVersion)
}

return types.Version, nil
}
2 changes: 1 addition & 1 deletion modules/apps/transfer/types/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import (
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
)

// IBC channel sentinel errors
// IBC transfer sentinel errors
var (
ErrInvalidPacketTimeout = sdkerrors.Register(ModuleName, 2, "invalid packet timeout")
ErrInvalidDenomForTransfer = sdkerrors.Register(ModuleName, 3, "invalid denomination for cross-chain transfer")
Expand Down
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)

// AppVersion implements the Query/AppVersion gRPC method
func (q Keeper) AppVersion(c context.Context, req *types.QueryAppVersionRequest) (*types.QueryAppVersionResponse, 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.Ordering, req.ConnectionId, req.PortId, *req.Counterparty, req.ProposedVersion)
if err != nil {
return nil, status.Errorf(codes.InvalidArgument, sdkerrors.Wrap(err, "version negotation failed").Error())
}

return types.NewQueryAppVersionResponse(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
}
103 changes: 103 additions & 0 deletions modules/core/05-port/keeper/grpc_query_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
package keeper_test

import (
"fmt"

sdk "github.com/cosmos/cosmos-sdk/types"

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

func (suite *KeeperTestSuite) TestAppVersion() {
var (
req *types.QueryAppVersionRequest
expVersion string
)

testCases := []struct {
msg string
malleate func()
expPass bool
}{
{
"empty request",
func() {
req = nil
},
false,
},
{
"invalid port ID",
func() {
req = &types.QueryAppVersionRequest{
PortId: "",
}
},
false,
},
{
"module not found",
func() {
req = &types.QueryAppVersionRequest{
PortId: "mock-port-id",
}
},
false,
},
{
"version negotiation failure",
func() {

expVersion = mock.Version

req = &types.QueryAppVersionRequest{
PortId: "mock", // retrieves the mock testing module
Counterparty: &channeltypes.Counterparty{
PortId: "mock-port-id",
ChannelId: "mock-channel-id",
},
ProposedVersion: "invalid-proposed-version",
}
},
false,
},
{
"success",
func() {

expVersion = mock.Version

req = &types.QueryAppVersionRequest{
PortId: "mock", // retrieves the mock testing module
Counterparty: &channeltypes.Counterparty{
PortId: "mock-port-id",
ChannelId: "mock-channel-id",
},
ProposedVersion: mock.Version,
}
},
true,
},
}

for _, tc := range testCases {
suite.Run(fmt.Sprintf("Case %s", tc.msg), func() {
suite.SetupTest() // reset

tc.malleate()

ctx := sdk.WrapSDKContext(suite.ctx)
res, err := suite.keeper.AppVersion(ctx, req)

if tc.expPass {
suite.Require().NoError(err)
suite.Require().NotNil(res)
suite.Require().Equal(expVersion, res.Version)
} else {
suite.Require().Error(err)
}
})
}
}
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)
}
12 changes: 12 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,16 @@ type IBCModule interface {
packet channeltypes.Packet,
relayer sdk.AccAddress,
) error

// NegotiateAppVersion performs application version negotiation given the provided channel ordering, connectionID, portID, counterparty and proposed version.
// An error is returned if version negotiation cannot be performed. For example, an application module implementing this interface
// may decide to return an error in the event of the proposed version being incompatible with it's own
NegotiateAppVersion(
ctx sdk.Context,
order channeltypes.Order,
connectionID string,
portID string,
counterparty channeltypes.Counterparty,
proposedVersion string,
) (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

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

0 comments on commit 85b4383

Please sign in to comment.