Skip to content

Commit

Permalink
Merge branch 'main' of github.com:cosmos/ibc-go into colin/640-improv…
Browse files Browse the repository at this point in the history
…e-app-version-negotiation
  • Loading branch information
colin-axner committed Dec 21, 2021
2 parents c3020ca + c060673 commit 16b567d
Show file tree
Hide file tree
Showing 12 changed files with 454 additions and 85 deletions.
34 changes: 32 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,9 @@ Ref: https://keepachangelog.com/en/1.0.0/

### API Breaking

* (core) [\#629](https://github.com/cosmos/ibc-go/pull/629) Removes the GetProofSpecs from the ClientState interface. This function was previously unused by core IBC.
* (core) [\#629](https://github.com/cosmos/ibc-go/pull/629) Removes the `GetProofSpecs` from the ClientState interface. This function was previously unused by core IBC.
* (transfer) [\#517](https://github.com/cosmos/ibc-go/pull/517) Separates the ICS 26 callback functions from `AppModule` into a new type `IBCModule` for ICS 20 transfer.
* (modules/core/02-client) [\#536](https://github.com/cosmos/ibc-go/pull/536) GetSelfConsensusState return type changed from bool to error.
* (modules/core/02-client) [\#536](https://github.com/cosmos/ibc-go/pull/536) `GetSelfConsensusState` return type changed from bool to error.
* (channel) [\#644](https://github.com/cosmos/ibc-go/pull/644) Removes `CounterpartyHops` function from the ChannelKeeper.

### State Machine Breaking
Expand All @@ -54,13 +54,23 @@ Ref: https://keepachangelog.com/en/1.0.0/
* [\#383](https://github.com/cosmos/ibc-go/pull/383) Adds helper functions for merging and splitting middleware versions from the underlying app version.
* (modules/core/05-port) [\#288](https://github.com/cosmos/ibc-go/issues/288) Making the 05-port keeper function IsBound public. The IsBound function checks if the provided portID is already binded to a module.
* (channel) [\#644](https://github.com/cosmos/ibc-go/pull/644) Adds `GetChannelConnection` to the ChannelKeeper. This function returns the connectionID and connection state associated with a channel.
* (channel) [\647](https://github.com/cosmos/ibc-go/pull/647) Reorganizes channel handshake handling to set channel state after IBC application callbacks.

### Features

* [\#432](https://github.com/cosmos/ibc-go/pull/432) Introduce `MockIBCApp` struct to the mock module. Allows the mock module to be reused to perform custom logic on each IBC App interface function. This might be useful when testing out IBC applications written as middleware.
* [\#380](https://github.com/cosmos/ibc-go/pull/380) Adding the Interchain Accounts module v1

### Bug Fixes

## [v2.0.2](https://github.com/cosmos/ibc-go/releases/tag/v2.0.2) - 2021-12-15

### Dependencies

* [\#589](https://github.com/cosmos/ibc-go/pull/589) Bump SDK version to v0.44.5

### Bug Fixes

* (modules/core) [\#603](https://github.com/cosmos/ibc-go/pull/603) Fix module name emitted as part of `OnChanOpenInit` event. Replacing `connection` module name with `channel`.

## [v2.0.1](https://github.com/cosmos/ibc-go/releases/tag/v2.0.1) - 2021-12-05
Expand Down Expand Up @@ -94,6 +104,16 @@ Ref: https://keepachangelog.com/en/1.0.0/

* [\#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.2.5](https://github.com/cosmos/ibc-go/releases/tag/v1.2.5) - 2021-12-15

### Dependencies

* [\#589](https://github.com/cosmos/ibc-go/pull/589) Bump SDK version to v0.44.5

### Bug Fixes

* (modules/core) [\#603](https://github.com/cosmos/ibc-go/pull/603) Fix module name emitted as part of `OnChanOpenInit` event. Replacing `connection` module name with `channel`.

## [v1.2.4](https://github.com/cosmos/ibc-go/releases/tag/v1.2.4) - 2021-12-05

### Dependencies
Expand Down Expand Up @@ -141,6 +161,16 @@ Ref: https://keepachangelog.com/en/1.0.0/

* [\#386](https://github.com/cosmos/ibc-go/pull/386) Bump [tendermint](https://github.com/tendermint/tendermint) from v0.34.12 to v0.34.13.

## [v1.1.5](https://github.com/cosmos/ibc-go/releases/tag/v1.1.5) - 2021-12-15

### Dependencies

* [\#589](https://github.com/cosmos/ibc-go/pull/589) Bump SDK version to v0.44.5

### Bug Fixes

* (modules/core) [\#603](https://github.com/cosmos/ibc-go/pull/603) Fix module name emitted as part of `OnChanOpenInit` event. Replacing `connection` module name with `channel`.

## [v1.1.4](https://github.com/cosmos/ibc-go/releases/tag/v1.1.4) - 2021-12-05

### Dependencies
Expand Down
221 changes: 221 additions & 0 deletions docs/app_modules/interchain-accounts/ica_auth.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,221 @@
<!--
order: 3
-->

# Building an ICA authentication module

The controller module is used for account registration and packet sending.
It executes only logic required of all controllers of interchain accounts.
The type of authentication used to manage the interchain accounts remains unspecified.
There may exist many different types of authentication which are desirable for different use cases.
Thus the purpose of the authentication module is to wrap the controller module with custom authentication logic.

In ibc-go, authentication modules are connected to the controller chain via a middleware stack.
The controller module is implemented as [middleware](https://github.com/cosmos/ibc/tree/master/spec/app/ics-030-middleware) and the authentication module is connected to the controller module as the base application of the middleware stack.
To implement an authentication module, the `IBCModule` interface must be fulfilled.
By implementing the controller module as middleware, any amount of authentication modules can be created and connected to the controller module without writing redundant code.

The authentication module must:
- Authenticate interchain account owners
- Track the associated interchain account address for an owner
- Claim the channel capability in `OnChanOpenInit`
- Send packets on behalf of an owner (after authentication)

### IBCModule implementation

The following `IBCModule` callbacks must be implemented with appropriate custom logic:

```go
// OnChanOpenInit implements the IBCModule interface
func (im IBCModule) OnChanOpenInit(
ctx sdk.Context,
order channeltypes.Order,
connectionHops []string,
portID string,
channelID string,
chanCap *capabilitytypes.Capability,
counterparty channeltypes.Counterparty,
version string,
) error {
// the authentication module *must* claim the channel capability on OnChanOpenInit
if err := im.keeper.ClaimCapability(ctx, chanCap, host.ChannelCapabilityPath(portID, channelID)); err != nil {
return err
}

// perform custom logic

return nil
}

// OnChanOpenAck implements the IBCModule interface
func (im IBCModule) OnChanOpenAck(
ctx sdk.Context,
portID,
channelID string,
counterpartyVersion string,
) error {
// perform custom logic

return nil
}

// OnChanCloseConfirm implements the IBCModule interface
func (im IBCModule) OnChanCloseConfirm(
ctx sdk.Context,
portID,
channelID string,
) error {
// perform custom logic

return nil
}

// OnAcknowledgementPacket implements the IBCModule interface
func (im IBCModule) OnAcknowledgementPacket(
ctx sdk.Context,
packet channeltypes.Packet,
acknowledgement []byte,
relayer sdk.AccAddress,
) error {
// perform custom logic

return nil
}

// OnTimeoutPacket implements the IBCModule interface.
func (im IBCModule) OnTimeoutPacket(
ctx sdk.Context,
packet channeltypes.Packet,
relayer sdk.AccAddress,
) error {
// perform custom logic

return nil
}
```

**Note**: The channel capability must be claimed by the authentication module in `OnChanOpenInit` otherwise the authentication module will not be able to send packets on the channel created for the associated interchain account.

The following functions must be defined to fulfill the `IBCModule` interface, but they will never be called by the controller module so they may error or panic.

```go
// OnChanOpenTry implements the IBCModule interface
func (im IBCModule) OnChanOpenTry(
ctx sdk.Context,
order channeltypes.Order,
connectionHops []string,
portID,
channelID string,
chanCap *capabilitytypes.Capability,
counterparty channeltypes.Counterparty,
version,
counterpartyVersion string,
) error {
panic("UNIMPLEMENTED")
}

// OnChanOpenConfirm implements the IBCModule interface
func (im IBCModule) OnChanOpenConfirm(
ctx sdk.Context,
portID,
channelID string,
) error {
panic("UNIMPLEMENTED")
}

// OnChanCloseInit implements the IBCModule interface
func (im IBCModule) OnChanCloseInit(
ctx sdk.Context,
portID,
channelID string,
) error {
panic("UNIMPLEMENTED")
}

// OnRecvPacket implements the IBCModule interface. A successful acknowledgement
// is returned if the packet data is succesfully decoded and the receive application
// logic returns without error.
func (im IBCModule) OnRecvPacket(
ctx sdk.Context,
packet channeltypes.Packet,
relayer sdk.AccAddress,
) ibcexported.Acknowledgement {
panic("UNIMPLEMENTED")
}

// NegotiateAppVersion implements the IBCModule interface
func (im IBCModule) NegotiateAppVersion(
ctx sdk.Context,
order channeltypes.Order,
connectionID string,
portID string,
counterparty channeltypes.Counterparty,
proposedVersion string,
) (string, error) {
panic("UNIMPLEMENTED")
}
```

## `InitInterchainAccount`

The authentication module can begin registering interchain accounts by calling `InitInterchainAccount`:

```go
if err := keeper.icaControllerKeeper.InitInterchainAccount(ctx, connectionID, counterpartyConnectionID, owner.String()); err != nil {
return err
}

return nil
```

## `TrySendTx`

The authentication module can attempt to send a packet by calling `TrySendTx`:
```go

// Authenticate owner
// perform custom logic

// Lookup portID based on interchain account owner address
portID, err := icatypes.GeneratePortID(owner.String(), connectionID, counterpartyConnectionID)
if err != nil {
return err
}

channelID, found := keeper.icaControllerKeeper.GetActiveChannelID(ctx, portID)
if !found {
return sdkerrors.Wrapf(icatypes.ErrActiveChannelNotFound, "failed to retrieve active channel for port %s", portId)
}

// Obtain the channel capability.
// The channel capability should have been claimed by the authentication module in OnChanOpenInit
chanCap, found := keeper.scopedKeeper.GetCapability(ctx, host.ChannelCapabilityPath(portID, channelID))
if !found {
return sdkerrors.Wrap(channeltypes.ErrChannelCapabilityNotFound, "module does not own channel capability")
}

// Obtain data to be sent to the host chain.
// In this example, the owner of the interchain account would like to send a bank MsgSend to the host chain.
// The appropriate serialization function should be called. The host chain must be able to deserialize the transaction.
// If the host chain is using the ibc-go host module, `SerializeCosmosTx` should be used.
msg := &banktypes.MsgSend{FromAddress: fromAddr, ToAddress: toAddr, Amount: amt}
data, err := icatypes.SerializeCosmosTx(keeper.cdc, []sdk.Msg{msg})
if err != nil {
return err
}

// Construct packet data
packetData := icatypes.InterchainAccountPacketData{
Type: icatypes.EXECUTE_TX,
Data: data,
}

_, err = keeper.icaControllerKeeper.TrySendTx(ctx, chanCap, p, packetData)
```

The data within an `InterchainAccountPacketData` must be serialized using a format supported by the host chain.
If the host chain is using the ibc-go host chain submodule, `SerializeCosmosTx` should be used. If the `InterchainAccountPacketData.Data` is serialized using a format not support by the host chain, the packet will not be successfully received.

### Integration into `app.go` file

To integrate the authentication module into your chain, please follow the steps outlined above in [app.go integration](./integration.md#example-integration).
27 changes: 27 additions & 0 deletions docs/app_modules/interchain-accounts/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,31 @@
order: 1
-->

# Overview

Learn about what the Interchain Accounts module is, and how to build custom modules that utilize Interchain Accounts functionality {synopsis}


# What is the Interchain Accounts module?

Interchain Accounts is the Cosmos SDK implementation of the ICS-27 protocol, which enables cross-chain account management built upon IBC. Chains using the Interchain Accounts module can programmatically create accounts on other chains and control these accounts via IBC transactions.

Interchain Accounts exposes a simple-to-use API which means IBC application developers do not require an in-depth knowledge of the underlying low-level details of IBC or the ICS-27 protocol.

Developers looking to build upon Interchain Accounts must write custom logic in their own IBC application module, called authentication modules.

- How is an interchain account different than a regular account?

Regular accounts use a private key to sign transactions on-chain. Interchain Accounts are instead controlled programmatically by separate chains via IBC transactions. Interchain Accounts are implemented as sub-accounts of the interchain accounts module account.

# Concepts

*Host Chain*: The chain where the interchain account is registered. The host chain listens for IBC packets from a controller chain which should contain instructions (e.g. cosmos SDK messages) for which the interchain account will execute.

*Controller Chain*: The chain registering and controlling an account on a host chain. The controller chain sends IBC packets to the host chain to control the account. A controller chain must have at least one interchain accounts authentication module in order to act as a controller chain.

*Authentication Module*: A custom IBC application module on the controller chain that uses the Interchain Accounts module API to build custom logic for the creation & management of interchain accounts. For a controller chain to utilize the interchain accounts module functionality, an authentication module is required.

*Interchain Account*: An account on a host chain. An interchain account has all the capabilities of a normal account. However, rather than signing transactions with a private key, a controller chain's authentication module will send IBC packets to the host chain which signals what transactions the interchain account should execute.


8 changes: 8 additions & 0 deletions docs/migrations/v2-to-v3.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,14 @@ ICS27 Interchain Accounts has been added as a supported IBC application of ibc-g

## IBC Apps

### Channel state will not be set before application callback

The channel handshake logic has been reorganized within core IBC.
Channel state will not be set in state after the application callback is performed.
Applications must rely only on the passed in channel parameters instead of querying the channel keeper for channel state.

### IBC application callbacks moved from `AppModule` to `IBCModule`

Previously, IBC module callbacks were apart of the `AppModule` type.
The recommended approach is to create an `IBCModule` type and move the IBC module callbacks from `AppModule` to `IBCModule` in a separate file `ibc_module.go`.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ func (k Keeper) OnChanOpenInit(
return sdkerrors.Wrapf(err, "expected format %s, got %s", icatypes.ControllerPortFormat, portID)
}

if err := k.validateControllerPortParams(ctx, channelID, portID, connSequence, counterpartyConnSequence); err != nil {
if err := k.validateControllerPortParams(ctx, connectionHops, connSequence, counterpartyConnSequence); err != nil {
return sdkerrors.Wrapf(err, "failed to validate controller port %s", portID)
}

Expand Down Expand Up @@ -104,8 +104,9 @@ func (k Keeper) OnChanCloseConfirm(

// validateControllerPortParams asserts the provided connection sequence and counterparty connection sequence
// match that of the associated connection stored in state
func (k Keeper) validateControllerPortParams(ctx sdk.Context, channelID, portID string, connectionSeq, counterpartyConnectionSeq uint64) error {
connectionID, connection, err := k.channelKeeper.GetChannelConnection(ctx, portID, channelID)
func (k Keeper) validateControllerPortParams(ctx sdk.Context, connectionHops []string, connectionSeq, counterpartyConnectionSeq uint64) error {
connectionID := connectionHops[0]
connection, err := k.channelKeeper.GetConnection(ctx, connectionID)
if err != nil {
return err
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,13 +59,6 @@ func (suite *KeeperTestSuite) TestOnChanOpenInit() {
},
false,
},
{
"channel not found",
func() {
path.EndpointA.ChannelID = "invalid-channel-id"
},
false,
},
{
"connection not found",
func() {
Expand Down
7 changes: 4 additions & 3 deletions modules/apps/27-interchain-accounts/host/keeper/handshake.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ func (k Keeper) OnChanOpenTry(
return sdkerrors.Wrapf(err, "expected format %s, got %s", icatypes.ControllerPortFormat, counterparty.PortId)
}

if err := k.validateControllerPortParams(ctx, channelID, portID, connSequence, counterpartyConnSequence); err != nil {
if err := k.validateControllerPortParams(ctx, connectionHops, connSequence, counterpartyConnSequence); err != nil {
return sdkerrors.Wrapf(err, "failed to validate controller port %s", counterparty.PortId)
}

Expand Down Expand Up @@ -104,8 +104,9 @@ func (k Keeper) OnChanCloseConfirm(

// validateControllerPortParams asserts the provided connection sequence and counterparty connection sequence
// match that of the associated connection stored in state
func (k Keeper) validateControllerPortParams(ctx sdk.Context, channelID, portID string, connectionSeq, counterpartyConnectionSeq uint64) error {
connectionID, connection, err := k.channelKeeper.GetChannelConnection(ctx, portID, channelID)
func (k Keeper) validateControllerPortParams(ctx sdk.Context, connectionHops []string, connectionSeq, counterpartyConnectionSeq uint64) error {
connectionID := connectionHops[0]
connection, err := k.channelKeeper.GetConnection(ctx, connectionID)
if err != nil {
return err
}
Expand Down
Loading

0 comments on commit 16b567d

Please sign in to comment.