Crates.io | pallet-ibc |
lib.rs | pallet-ibc |
version | 2.0.0-pre.2 |
source | src |
created_at | 2020-10-18 10:33:34.037269 |
updated_at | 2020-11-29 12:52:41.157047 |
description | An IBC implementation on Substrate. |
homepage | https://cdot.network |
repository | https://github.com/cdot-network/substrate-ibc/ |
max_upload_size | |
id | 302013 |
size | 125,488 |
This project is funded by Interchain Foundation.
This pallet implements the standard IBC protocol.
The goal of this pallet is to allow the blockchains built on Substrate to gain the ability to interact with other chains in a trustless way via IBC protocol, no matter what consensus the counterparty chains use.
This project is currently in an early stage and will eventually be submitted to upstream.
Some components in ICS spec are implemented to support a working demo (https://github.com/cdot-network/ibc-demo), but not fully implemented as the spec:
Here is a demo for showing how to utilize this pallet, which initializes a series of steps for cross-chain communication, from client creation to sending packet data.
This pallet does not depend on any externally defined traits.
This pallet does not depend on any other FRAME pallet or externally developed modules.
Cargo.toml
To add this pallet to your runtime, simply include the following to your runtime's Cargo.toml
file:
[dependencies.pallet-ibc]
default_features = false
git = 'https://github.com/cdot-network/substrate-ibc.git'
and update your runtime's std
feature to include this pallet:
std = [
# --snip--
'pallet-ibc/std',
]
lib.rs
A custom structure that implements the pallet_ibc::ModuleCallbacks must be defined to dispatch messages to receiving module.
pub struct ModuleCallbacksImpl;
impl pallet_ibc::ModuleCallbacks for ModuleCallbacksImpl {
# --snip--
}
You should implement it's trait like so:
/// Used for test_module
impl pallet_ibc::Trait for Runtime {
type Event = Event;
type ModuleCallbacks = ModuleCallbacksImpl;
}
and include it in your construct_runtime!
macro:
Ibc: pallet_ibc::{Module, Call, Storage, Event<T>},
This pallet does not have any genesis configuration.
In the ibc-demo repo, substrate-subxt invokes the pallet's callable functions by the macro substrate_subxt_proc_macro::Call
.
Let's take the function test_create_client
as an example. Client extends the function
// in https://github.com/cdot-network/ibc-demo/blob/master/pallets/template/src/lib.rs
pub fn test_create_client(
origin,
identifier: H256,
height: u32,
set_id: SetId,
authorities: AuthorityList,
root: H256
) -> dispatch::DispatchResult {
...
}
by
// https://github.com/cdot-network/ibc-demo/blob/master/calls/src/template.rs
#[derive(Encode, Call)]
pub struct TestCreateClientCall<T: TemplateModule> {
pub _runtime: PhantomData<T>,
pub identifier: H256,
pub height: u32,
pub set_id: SetId,
pub authority_list: AuthorityList,
pub root: H256,
}
Therefore,
// https://github.com/cdot-network/ibc-demo/blob/master/cli/src/main.rs
client
.test_create_client(...)
can invoke the test_create_client
function.
Please refer to document substrate_subxt_proc_macro::Call for details.
In unit test, we comply with the substrate's document Runtime Tests.
The mock enviroment is built in mock.rs; In tests.rs, the pallet's callable functions are tested.
Datagram::ClientUpdate
arm:// https://github.com/cdot-network/substrate-ibc/blob/master/src/lib.rs
pub fn handle_datagram(datagram: Datagram) -> dispatch::DispatchResult {
match datagram {
Datagram::ClientUpdate { identifier, header } => { // <--- "Datagram::ClientUpdate" will be matached
ConsensusStates
.// https://github.com/cdot-network/substrate-ibc/blob/master/src/lib.rs
ConsensusStates::insert((identifier, header.height), new_consensus_state);
As the table in Opening Handshake, the handshakes between 2 chains(A & B) comprises 4 steps.
Initiator | Datagram | Chain acted upon | Prior state (A, B) | Posterior state (A, B) |
---|---|---|---|---|
Actor | ConnOpenInit |
A | (none, none) | (INIT, none) |
Relayer | ConnOpenTry |
B | (INIT, none) | (INIT, TRYOPEN) |
Relayer | ConnOpenAck |
A | (INIT, TRYOPEN) | (OPEN, TRYOPEN) |
Relayer | ConnOpenConfirm |
B | (OPEN, TRYOPEN) | (OPEN, OPEN) |
It's done by an actor, who invokes the function conn_open_init
in Chain A.
// https://github.com/cdot-network/substrate-ibc/blob/master/src/lib.rs
pub fn conn_open_init(
identifier: H256,
desired_counterparty_connection_identifier: H256,
client_id: H256,
counterparty_client_id: H256,
) -> dispatch::DispatchResult {
...
}
The relayer detects the INIT
state of chain A's connection, then try to set chain B's connection's state to TRYOPEN
by invoking the chain B's function pub fn handle_datagram(datagram: Datagram)
,
whose arm Datagram::ConnOpenTry
will be matached.
// https://github.com/cdot-network/substrate-ibc/blob/master/src/lib.rs
pub fn handle_datagram(datagram: Datagram) -> dispatch::DispatchResult {
match datagram {
...
Datagram::ConnOpenTry {
...
}
The relayer detects the TRYOPEN
of chain B's connection, then try to set chain A's connection's state to OPEN
by invoking the chain A's function pub fn handle_datagram(datagram: Datagram)
,
whose arm Datagram::ConnOpenAck
will be matached.
// https://github.com/cdot-network/substrate-ibc/blob/master/src/lib.rs
pub fn handle_datagram(datagram: Datagram) -> dispatch::DispatchResult {
match datagram {
...
Datagram::ConnOpenAck {
...
}
The relayer detects the OPEN
of chain A's connection, then try to set chain B's connection's state to OPEN
by invoking the chain B's function pub fn handle_datagram(datagram: Datagram)
,
whose arm Datagram::ConnOpenConfirm
will be matached.
// https://github.com/cdot-network/substrate-ibc/blob/master/src/lib.rs
pub fn handle_datagram(datagram: Datagram) -> dispatch::DispatchResult {
match datagram {
...
Datagram::ConnOpenConfirm {
...
}
After the 2 chains(A & B) finish connection handshakes, they are able to build a channel by handshakes on the connection.
As the table in Channel lifecycle management, the handshakes between 2 chains(A & B) comprises 4 steps.
Initiator | Datagram | Chain acted upon | Prior state (A, B) | Posterior state (A, B) |
---|---|---|---|---|
Actor | ChanOpenInit | A | (none, none) | (INIT, none) |
Relayer | ChanOpenTry | B | (INIT, none) | (INIT, TRYOPEN) |
Relayer | ChanOpenAck | A | (INIT, TRYOPEN) | (OPEN, TRYOPEN) |
Relayer | ChanOpenConfirm | B | (OPEN, TRYOPEN) | (OPEN, OPEN) |
It's done by an actor, who invokes the function chan_open_init
in Chain A.
// https://github.com/cdot-network/substrate-ibc/blob/master/src/lib.rs
pub fn chan_open_init(
...
) -> dispatch::DispatchResult {
...
}
The relayer detects the INIT
state of chain A's channel, then try to set chain B's channel's state to TRYOPEN
by invoking the chain B's function pub fn handle_datagram(datagram: Datagram)
,
whose arm Datagram::ChanOpenTry
will be matached.
// https://github.com/cdot-network/substrate-ibc/blob/master/src/lib.rs
pub fn handle_datagram(datagram: Datagram) -> dispatch::DispatchResult {
match datagram {
...
Datagram::ChanOpenTry {
...
}
The relayer detects the TRYOPEN
of chain B's channel, then try to set chain A's channel's state to OPEN
by invoking the chain A's function pub fn handle_datagram(datagram: Datagram)
,
whose arm Datagram::ChanOpenAck
will be matached.
// https://github.com/cdot-network/substrate-ibc/blob/master/src/lib.rs
pub fn handle_datagram(datagram: Datagram) -> dispatch::DispatchResult {
match datagram {
...
Datagram::ChanOpenAck {
...
}
The relayer detects the OPEN
of chain A's channel, then try to set chain B's channel's state to OPEN
by invoking the chain B's function pub fn handle_datagram(datagram: Datagram)
,
whose arm Datagram::ChanOpenConfirm
will be matached.
// https://github.com/cdot-network/substrate-ibc/blob/master/src/lib.rs
pub fn handle_datagram(datagram: Datagram) -> dispatch::DispatchResult {
match datagram {
...
Datagram::ChanOpenConfirm {
...
}
After the 2 chains(A & B) finish channel handshakes, they are able to send packets to each other on the channel.
As the flowchart in Packet flow & handling, the standard flow of sending a packet from chain A to chain B comprises 3 steps.
The callable function send_packet
in Chain A's ibc pallet sends a packet by depositing an RawEvent::SendPacket
event.
// https://github.com/cdot-network/substrate-ibc/blob/master/src/lib.rs
pub fn send_packet(packet: Packet) -> dispatch::DispatchResult {
...
Self::deposit_event(RawEvent::SendPacket(
...
));
}
The relayer detects chain A's RawEvent::SendPacket
event, then try to call chain B's function pub fn handle_datagram(datagram: Datagram)
,
and match its arm Datagram::PacketRecv
, for chain B to receive the packet.
After receiving the packet, chain B deposits an event RawEvent::RecvPacket
as acknowledgement.
// https://github.com/cdot-network/substrate-ibc/blob/master/src/lib.rs
pub fn handle_datagram(datagram: Datagram) -> dispatch::DispatchResult {
match datagram {
...
Datagram::PacketRecv {
...
Self::deposit_event(RawEvent::RecvPacket(
...
));
}
The relayer detects chain B's RawEvent::RecvPacket
event, then try to call chain A's function pub fn handle_datagram(datagram: Datagram)
,
and match its arm Datagram::PacketAcknowledgement
, for chain A to process the acknowledgement.
// https://github.com/cdot-network/substrate-ibc/blob/master/src/lib.rs
pub fn handle_datagram(datagram: Datagram) -> dispatch::DispatchResult {
match datagram {
...
Datagram::PacketAcknowledgement {
...
}
You can view the reference docs for this pallet by running:
cargo doc --open
or by visiting this site: https://docs.rs/pallet-ibc