Giter VIP home page Giter VIP logo

cw-ica-controller's Introduction

CosmWasm ICA Controller Contract

E2E Status Tag License: Apache-2.0

cw-ica-controller

This is a CosmWasm smart contract that communicates with the golang ica/host module on the counterparty chain to create and manage one interchain account. This contract can also execute callbacks based on the result of the interchain account transaction. Because this is a CosmWasm implementation of the entire ICA controller, the chain that this contract is deployed on need not have the ICA module enabled. Moreover, the counterparty chain need not have CosmWasm support. This contract uses CosmWasm v2.1 but can be deployed on chains that support CosmWasm v1.4+.

A documentation website for this contract is here.

Table of Contents

Usage

The following is a brief overview of the contract's functionality. (You can also see the various ways this contract can be used in the end to end tests in the e2e directory.)

Create an interchain account

This contract provides two ways to create an interchain account:

  1. Using InstantiateMsg
  2. Using ExecuteMsg::CreateChannel

Using InstantiateMsg

This contract only accepts MsgChannelOpenInit messages sent by itself. Relayers can never initiate a channel handshake with this contract.

InstantiateMsg always initiates the channel handshake and this is why channel_open_init_options field is not optional.

/// The message to instantiate the ICA controller contract.
#[cw_serde]
pub struct InstantiateMsg {
    /// The address of the owner of the ICA application.
    /// If not specified, the sender is the owner.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub owner: Option<String>,
    /// The options to initialize the IBC channel upon contract instantiation.
    pub channel_open_init_options: options::ChannelOpenInitOptions,
    /// The contract address that the channel and packet lifecycle callbacks are sent to.
    /// If not specified, then no callbacks are sent.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub send_callbacks_to: Option<String>,
}

Using ExecuteMsg::CreateChannel

If the channel_open_init_options field in InstantiateMsg was malformed in a way that prevents the channel handshake from succeeding, the contract owner can submit a ExecuteMsg::CreateChannel with a new channel_open_init_options.

pub enum ExecuteMsg {
    /// `CreateChannel` makes the contract submit a stargate MsgChannelOpenInit to the chain.
    /// This is a wrapper around [`options::ChannelOpenInitOptions`] and thus requires the
    /// same fields. If not specified, then the options specified in the contract instantiation
    /// are used.
    CreateChannel {
        /// The options to initialize the IBC channel.
        /// If not specified, the options specified in the contract instantiation are used.
        /// Must be `None` if the sender is not the owner.
        #[serde(skip_serializing_if = "Option::is_none")]
        channel_open_init_options: Option<options::ChannelOpenInitOptions>,
    },

    // ...
}

In case the channel was closed due to a timeout, the contract owner can submit a ExecuteMsg::CreateChannel with channel_open_init_options: None to create a channel with the same channel_open_init_options as the last channel opening. Learn more about channel closing and reopening here.

Execute an interchain account transaction

ExecuteMsg::SendCosmosMsgs is used to commit a packet to be sent to the host chain. It accepts cosmwasm_std::CosmosMsgs to be sent to the host chain. (The contract then these messages to protobuf messages and sends them to the host chain. You can execute any custom message using CosmosMsg::Stargate).

In CosmWasm contracts, CosmosMsg are used to execute transactions on the chain that the contract is deployed on. In this contract, we use CosmosMsgs to execute transactions on the host (counterparty) chain. This is done by converting the CosmosMsgs to a protobuf ICA tx. The ICA tx is then sent to the host chain. The host chain then executes the ICA tx and sends the result back to this contract.

This execute message allows the user to submit an array of cosmwasm_std::CosmosMsg which are then converted by the contract to an atomic ICA tx.

pub enum ExecuteMsg {
    // ...

    /// `SendCosmosMsgs` converts the provided array of [`CosmosMsg`] to an ICA tx and sends them to the ICA host.
    /// [`CosmosMsg::Stargate`] and [`CosmosMsg::Wasm`] are only supported if the [`TxEncoding`](crate::ibc::types::metadata::TxEncoding) is [`TxEncoding::Protobuf`](crate::ibc::types::metadata::TxEncoding).
    ///
    /// **This is the recommended way to send messages to the ICA host.**
    SendCosmosMsgs {
        /// The stargate messages to convert and send to the ICA host.
        #[serde_as(deserialize_as = "serde_with::DefaultOnNull")]
        messages: Vec<CosmosMsg>,
        /// The stargate queries to convert and send to the ICA host.
        /// The queries are executed after the messages.
        #[cfg(feature = "query")]
        #[serde(skip_serializing_if = "Vec::is_empty")]
        #[serde(default)]
        #[serde_as(deserialize_as = "serde_with::DefaultOnNull")]
        queries: Vec<cosmwasm_std::QueryRequest<cosmwasm_std::Empty>>,
        /// Optional memo to include in the ibc packet.
        #[serde(skip_serializing_if = "Option::is_none")]
        packet_memo: Option<String>,
        /// Optional timeout in seconds to include with the ibc packet.
        /// If not specified, the [default timeout](crate::ibc::types::packet::DEFAULT_TIMEOUT_SECONDS) is used.
        #[serde(skip_serializing_if = "Option::is_none")]
        timeout_seconds: Option<u64>,
    },

    // ...
}

(CosmosMsg::Stargate allows the user to submit any protobuf message to the host chain.)

Here is an example execute message that delegates tokens to a validator on the host chain and then votes on a proposal (atomically).

{
  "send_cosmos_msgs":{
    "messages":[
      {
        "staking":{
          "delegate":{
            "validator":"validatorAddress",
            "amount":{
              "denom":"uatom",
              "amount":"10000000"
            }
          }
        }
      },
      {
        "gov":{
          "vote":{
            "proposal_id":1,
            "vote":"yes"
          }
        }
      }
    ]
  }
}

Querying the host chain

This contract also supports querying the host chain. To do this, you can submit a ExecuteMsg::SendCosmosMsgs with the queries field filled out. The queries are always executed after the messages, and their results are deserialized and returned in the callbacks.

This feature only works if the host (counterparty) chain is on ibc-go v7.5+. If the host chain is on an older version, then the packet will return an error acknowledgement.

Similarly to CosmosMsg, in CosmWasm contracts, QueryRequest are used to execute queries on the chain that the contract is deployed on. In this contract, we use QueryRequests to execute queries as transactions on the host (counterparty) chain. This is done by converting the QueryRequestss to a protobuf ICA tx. The ICA tx is then sent to the host chain. The host chain then executes the ICA tx and sends the result back to this contract.

Note that if both messages and queries are provided, the queries are executed after the messages.

Unlike the messages, not all query requests are supported, as query execution is not generally deterministic in CosmosSDK. See the documentation for the supported query requests here.

Execute a callback

This contract supports external contract callbacks. See src/types/callbacks.rs to learn what callbacks are supported. This contract currently only supports sending callbacks to a single contract. You register the callback contract address during instantiation, or update it later using ExecuteMsg::UpdateCallbackAddress.

The callback contract must include the following variant in its ExecuteMsg enum:

use cosmwasm_schema::cw_serde;
use cw_ica_controller::types::callbacks::IcaControllerCallbackMsg;

#[cw_serde]
pub enum ExecuteMsg {
    // ... other variants

    /// The callback message from the ICA controller contract.
    ReceiveIcaCallback(IcaControllerCallbackMsg),
}

Note that this crate also includes a proc-macro to add the ReceiveIcaCallback variant to the ExecuteMsg enum. This is done by adding the following macro to the callback contract:

use cosmwasm_schema::cw_serde;
use cw_ica_controller::helpers::ica_callback_execute;

#[ica_callback_execute]
#[cw_serde]
/// This is the execute message of the contract.
pub enum ExecuteMsg {
    // ... other variants
}

Any contract that imports the cw-ica-controller as a library needs to disable the default-features of the cw-ica-controller crate. This is because the default-features of the cw-ica-controller crate includes the CosmWasm entry points.

[dependencies]
cw-ica-controller = { version = "0.6.0", default-features = false }

Channel Closing and Reopening

Channel Closing

An ICA channel can be closed due to a timed out packet if the channel is ordered. Otherwise, the channel can be closed by the user by submitting a ExecuteMsg::CloseChannel message.

Channel Reopening

If the ICA channel is closed, the contract is then able to create a new channel with the same interchain account address, and continue to use the same interchain account. To do this, you submit a ExecuteMsg::CreateChannel. Note that the channel_open_init_options can be changed when creating a new channel. This is useful if the user wants to change the ordering of the channel.

Demo

This project was used in the Injective Illuminate Hackathon and XION ABSTRACTATHON winner projects Tokenized Interchain Accounts, Nomos Abstraction on Xion

Injective Illuminate Hackathon

Each NFT controls an interchain account. The following is a demo of the project:

XION ABSTRACTATHON

Buying and selling and NFT from Xion on Injective using Nomos SDK and ICA controller

Building

We use cosmwasm/optimizer docker image to build the contract. This project uses just as the task runner. To install just, run the following command:

cargo install just

To build the contract, run the following command:

just build-optimize

Testing

There are two kinds of tests in this repository: unit tests and end to end tests. The unit tests are located inside the rust files in the src directory. The end to end tests are located in the e2e directory.

Unit tests

In general, the unit tests are for testing the verification functions for the handshake, and for testing that the serializers and deserializers are working correctly. To run the unit tests, run:

just unit-tests

End to end tests

The end to end tests are for testing the contract's functionality in an environment mimicking production. To see whether or not it can perform the channel handshake, send packets, and execute callbacks. We achieve this by running two local chains, one for the contract, and one for the host chain. The relayer is then used to perform the channel handshake, and send packets. The contract then executes callbacks based on the result of the packet. To learn more about the end to end tests, see the Readme in the e2e directory.

Releases

This contract follows semantic versioning, but with the following deviations:

  • A major version will not be tagged until the contract is audited.
  • All API breaking changes and most state-machine breaking changes will result in a minor version bump.

Limitations

This contract is not meant to be used in production. It is meant to be used as a reference implementation for how to build a CosmWasm contract that can communicate with the golang ica/host module. The following are some of the limitations of this contract:

  • The contract cannot create multiple interchain accounts. It can only create one.

Acknowledgements

Much thanks to Art3mix and CyberHoward for all the helpful discussions. Also thanks to 0xekez for their work on cw-ibc-example which was a great reference for CosmWasm IBC endpoints and interchaintest.

cw-ica-controller's People

Contributors

srdtrk avatar actions-user avatar ohadbachner avatar

Stargazers

Minh Đăng avatar ImanPJN avatar Kaan Yüksel avatar Hard-Nett avatar Harish Bhawnani avatar  avatar mrlp4 avatar stepit avatar shane.stars avatar  avatar  avatar Buse Sence avatar stake avatar Peter Blockman avatar Mc01.eth avatar Chanaka Karunarathne avatar Erdinç Özsertel avatar Joe Schnetzler avatar Miftahul Arifin avatar Chris avatar Reece Williams avatar Interchain Adair avatar Susannah Evans avatar Udit Vira avatar Gilberto Bittencourt avatar dzmitry-lahoda avatar Adam Tucker avatar LeTurt avatar Lumi avatar Javed Khan avatar C H avatar Ryan Gordon avatar magiodev.eth avatar seanrad.scrt avatar Damian Nolan avatar

Watchers

 avatar

cw-ica-controller's Issues

Add whitelist for channel opening

Description

Currently, the contract itself is the only address allowed to create a channel (except if chan_open_init_options is None). We could add a whitelist of (relayer) addresses who are allowed to open a new channel. This list should always contain the contract's address.

This would also allow us to remove the logic which allows any relayer to open the channel if chan_open_init_options is None!

Add a `CloseChannel` execute message

Summary

Add an execute message that instantiates the closing of the channel.

Description

This would be useful for channel reopening tests as currently the only way to get to a closed channel is to timeout a message. This makes the tests take longer, and more dependent on each other.

Challenges

I've attempted to implement this in many ways. I was able to get ibc_channel_close (::CloseInit) to execute, which did close the channel on wasmd. However, no matter how hard I tried, the channel end on simd remained open. Which leads me to think that this is a relayer issue.

Also, there is no use case I can think of to closing the channel on demand.

Use pedantic clippy for linting

Description

Standard clippy ignores a lot of pedantic warnings. Instead, have the contract use and comply with

cargo clippy -- -D clippy::pedantic -D clippy::nursery

Support UNORDERED channels

Description

Unordered ICA channels will be supported in ibc-go v8.1+ which is set to release this week.

when doing this, consider:

  • All callbacks
  • Make sure timeouts don't close the channel in internal state if UNORDERED
  • Add tests

Implement Channel Reopening

Problem Summary

Once a channel is closed due to timeout, or other reasons. Add the ability to reopen it.

Description

Once a channel is closed due to timeout, or other reasons, there should be a path to reopen it. This reopened channel should have the same ica address as the closed channel.

Tasks

  • Implement the feature in a feature branch.
  • Write end to end tests for it.

Add support for proto encoding type for tx's

Summary

IBC-Go's ICA module supports two types of encoding for tx's. This contract currently only supports json and not proto.

Description

IBC-Go's ICA module currently supports "proto" and "proto3json". This contract is currently only compatible with "proto3json" which was recently added. It is planned to be released in ibc-go v7.3.

In order for this contract to work in current mainnets, "proto" must be supported. The proposal is to add a configuration option during instantiate, and use the cosmos-sdk-proto crate for encoding.

Add auto channel reopening support

Recent security related minor releases of ibc-go has switched the order of timeout callbacks, allowing us to automatically initiate channel reopening.

Remove predefined action

Predefined action is good for debugging and testing but not for production. Instead move this to an owner contract used in testing.

Remove `allow_channel_open_init` from state

Description

This is included in the response to QueryMsg::GetContractState which may be confusing to the user since it is only used internally.

Either remove this from state or from the query response.

Turn the contract into library workspace

Problem Summary

This contract is simply a reference implementation. Instead, it should be a library people can use in their own contracts.

Description

This repo should become a library instead for people to use in their contracts. And the reference implementation should be an example within this library. The end to end tests should remain in the repo, and more unit tests can be added to the library.

Add CosmosMsg support for ICA Tx

Description

This contract currently only provides a single API for interchain txs to be sent, and that is ExecuteMsg::SendCustomIcaMessages. However, this API expects messages formatted in a non-standard way.

Proposal

Add a new API which is called something like ExecuteMsg::SendCosmosMsgs which expects an array of CosmosMsg. These cosmos messages should then be converted an ICA tx and sent.

Pros

  • Standard API.
  • Huge dev-ex improvement especially when it comes to proto encoded ICA channels with CosmosMsg::Stargate.

Cons

  • The types of stargate messages grow with the cosmwasm version. This adds maintenance burden.
  • This API might not work if proto json encoding is selected

Implement migration logic

Summary

implement the migrate entry point

Description

It would be good to allow migrations as this seems to be a user request. This needs to have many tests associated as we must ensure that the ibc channel is still functional after a migration. (I don't know much about migrations but it would be good to learn more and allow it here.)

  • Implement the migrate entry point properly.
  • Write e2e tests including:
    • Channel was open before upgrade.
    • Channel was closed before the upgrade, and reopened after.

EPIC: docusaurus docs website

Description

Have a docusaurus website which has the following:

  • Search bar
  • Overview
  • How it works
  • Demos
  • Integration
  • Deploy to github sites
  • CI job for checking links and deployment

Add a test for every CosmosMsg type supported

Description

#28 added support for cosmos msgs -> ICA tx. But not all message types are tested. They should be tested.

The following messages were tested anecdotally in cw-nft-ica: (only in protobuf encoding)

  • Send
  • IBC Transfer
  • Delegate
  • Undelegate
  • Redelegate
  • WithdrawDelegatorReward

The following messages are currently tested in the e2e suite:

  • Delegate
  • Vote

The following messages are not tested at all:

  • Wasm::*
  • VoteWeighted
  • Stargate
  • SetWithdrawAddress

Update interchaintest to v8

Description

This is needed to test with SDK v0.50 and ibc-go v8. It'd also be nice if we can run tests with SDK v0.47 and SDK v0.50 in the CI

Parallelize e2e tests

Problem Summary

Rather than having one monolithic e2e test, split it up into smaller tests that can be ran in parallel in github actions.

Description

Currently, all the end to end tests are in one monolithic test called TestIcaControllerContract as subtests. This means github actions cannot run these tests in parallel, leading to long waiting times for github actions to complete.

Remove stargate query support

Description

I've added stargate support for querying the counterparty_connection_id in case the user provides an empty version string.
This stargate query is not supported on every chain. I'd rather require the user to provide this (which is the case for contract created channels since v0.2.0). This feature is also harder to test since they don't work in every environment. I think this feature should be removed

Remove the `library` feature

Description

This contract was built on top of cw-template which comes with the library feature. External contracts and libraries that are meant to import this contract are supposed to import it with the library feature enabled which disables the entry point functions.

The reason why we cannot have entry point functions disabled by default is because the rust-optimizer itself always compiles with default features.

Proposal

CyberHoward suggested that we use an inverted feature called export. Which would have to be enabled by default to support rust-optimizer. This would improve the external libraries' devex since the package would work as intended if imported as

cw-ica-controller = { version = "...", default-features = false }

Create a tutorial

Description

  • Upload the contract's code on various chains
  • Write a tutorial involving a terminal client and relayer

Remove callback counter

Description

CallbackCounter was initially used in tests when the contract didn't have callback capabilities. It is now a redundant relic.

Proposal

The removal is pretty straightforward. The e2e tests need to be refactored not to use callback counter, I can see 2 ways forward:

  1. Simply remove callback counter assertions from the e2e tests.
  2. Use the owner testing contract to store the counter.
  3. Create a new callback testing contract with the counter.

Switch to hermes relayer in e2e tests

Description

The reason why I couldn't implement issue #5 is that at the time it seemed go relayer didn't support channel closing. Perhaps hermes can fix this. Also, hermes is the most commonly used relayer

Speed up e2e tests

Summary

The e2e tests are taking 10-15min to complete.

Proposal

I'd like to explore caching the go dependencies and docker images before explore speeding up the chain.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.