ibc-rs is a Rust library that implements the Inter Blockchain Communication (IBC) protocol. Ibc-rs was designed from the ground up to be independent from the underlying consensus. Want to use CometBFT? Sure! Near? Of course! Solana? You got it. This is achievable because through our ValidationContext
and ExecutionContext
traits, we abstract away how transactions look like, the store, and basically every other detail that is not native to IBC.
This article is going to be solely about ibc-rs, the implementation of the protocol. Before you keep reading, you might want a refresher on how IBC works. You can read more about IBC on the website, or in the ibc-go docs.
A core concept in IBC is clients. It is the core abstraction that keeps track of the counterparty chain and verifies proofs. There are many different types of clients. Although currently the most commonly used one is the tendermint light client, IBC's client abstraction is way more flexible than just that. It is not only flexible enough to let you implement light clients for any other consensus protocol; it can also be used to define a multisig protocol, where a set of trusted keys inform IBC of changes to the counterparty chain. In fact, this client exists, and is called the "solomachine" client in IBC parlance.
ibc-rs was architected in a way that lets client implementations live outside the core crate. This way, anyone can implement new light clients and have those live in separate crates. And in fact people are! Octopus Network wrote a solomachine client. Composable Finance wrote a Near light client, although it is currently written for an older version of ibc-rs. They are working on upgrading it to the latest version; stay tuned!
Applications are another core concept in IBC. This is where the business logic is meant to live. The most popular application is the token transfer application, also known as ICS 20. As the name suggests, this is the application that lets you send tokens across to other chains. However, IBC doesn't stop there. Applications are actually a quite powerful abstraction. You can define many different protocols, from controlling accounts on other chains (ICS 27), to atomic swaps (ICS 100).
ibc-rs currently only supports the token transfer app out of the box. However, similar to clients, ibc-rs was architected in a way that lets users write applications in separate crates. You simply need to ensure that your app implements the Module
trait, and you're good to go!
As a user of ibc-rs, the two primary traits that you will have to implement are ValidationContext
and ExecutionContext
. The first, ValidationContext
, asks you to implement methods that will be used when performing any validation that core IBC needs to do (achieved by calling validate()
). For example, one method asks you to retrieve a ClientState
object associated with a certain client at a given height. The second, ExecutionContext
, exposes additional methods that will be needed for ibc-rs to apply the state changes that executing a given IBC datagram requires (achieved by calling execute()
).
Now, why bother separating the "validation" and "execution" of IBC datagrams? It is to enable a larger class of possible chain architectures.
A great example of this is Namada. In a nutshell, for any given transaction, Namada allows users to define multiple validation steps, that they call "validity predicates". If any one of these validity predicates fail, then the whole transaction fails. Only if all validity predicates succeed then is the transaction actually executed. In the case of IBC datagrams, the IBC validation is only one of many possible validations that users might want to run. Since all validity predicates are independent, they are run in parallel, leading to a more efficient chain. Before we separated validation and execution, similar architectures would not be able to use ibc-rs; or at least be slowed down by it.
That is to say, you can still use ibc-rs even if you don't care about separating validation and execution! Internally, we sometimes call such chain architecture "SDK-like chains", referring to the Cosmos SDK. All you need to do is implement ValidationContext
and ExecutionContext
, and use dispatch()
to process IBC datagrams. This will perform validation, followed by execution.
Hence, the separation of validation and execution enabled a strictly larger set of architectures with no downsides for those who don't need distinction; we thus felt it resulted in a better library.
Of course, we're not done improving ibc-rs (will we ever be?). For one, we'd like to add support for middlewares. Middlewares sit between core IBC and an application, and allow developers to tweak how a base application works. A widely used middleware is Strangelove's Packet Forward Middleware (PFM), which sits between IBC core and the token transfer app, and allows to send tokens to a destination chain passing through another one. This is sometimes referred to as a "multi-hop channel" for tokens only.
Another feature we're considering is to offer an async API. The library currently offers only a synchronous API. If this is something that your team would benefit from, please reach out and let us know!
And this is probably the most important. We're building ibc-rs for you. If a feature is missing, or you'd like us to make some tweaks, please reach out! A great way is to open an issue on Github. We also hold monthly developer calls, which you can attend by joining our Google group. Feel free to jump in and let us know how we can help you.