3个版本
2.0.0-pre.2 | 2020年11月29日 |
---|---|
2.0.0-pre.1 | 2020年10月19日 |
2.0.0-pre.0 | 2020年10月18日 |
#51 in #ibc
110KB
2K SLoC
Substrate IBC Pallet(进行中)
该项目由Interchain Foundation资助。
目的
此模块实现了标准的IBC协议。
此模块的目标是允许基于Substrate构建的区块链通过IBC协议以无需信任的方式与其他链进行交互,无论对方链使用何种共识机制。
该项目目前处于早期阶段,最终将被提交到上游。
在ICS规范中的一些组件已实现以支持工作演示(https://github.com/cdot-network/ibc-demo),但尚未完全按照规范实现
- ics-002-client-semantics
- ics-003-connection-semantics
- ics-004-channel-and-packet-semantics
- ics-005-port-allocation
- ics-010-grandpa-client
- ics-018-relayer-algorithms
- ics-025-handler-interface
- ics-026-routing-module
以下是演示,展示了如何使用此模块,它初始化了一系列跨链通信步骤,从创建客户端到发送数据包。
依赖项
特质
此模块不依赖于任何外部定义的特质。
模块
此模块不依赖于任何其他FRAME模块或外部开发的模块。
安装
运行时 Cargo.toml
要将此模块添加到您的运行时,请将以下内容添加到您的运行时的Cargo.toml
文件中
[dependencies.pallet-ibc]
default_features = false
git = 'https://github.com/cdot-network/substrate-ibc.git'
并更新您的运行时的std
功能以包括此模块
std = [
# --snip--
'pallet-ibc/std',
]
运行时 lib.rs
必须定义一个自定义结构,该结构实现pallet_ibc::ModuleCallbacks,以将消息分派到接收模块。
pub struct ModuleCallbacksImpl;
impl pallet_ibc::ModuleCallbacks for ModuleCallbacksImpl {
# --snip--
}
您应该像这样实现它
/// Used for test_module
impl pallet_ibc::Trait for Runtime {
type Event = Event;
type ModuleCallbacks = ModuleCallbacksImpl;
}
并将其包含在您的construct_runtime!
宏中
Ibc: pallet_ibc::{Module, Call, Storage, Event<T>},
创世配置
此托盘没有任何创世配置。
如何与托盘交互
运行时
在 ibc-demo 仓库中,substrate-subxt 通过宏 substrate_subxt_proc_macro::Call
调用托盘的可调用函数。
以函数 test_create_client
为例。 Client 扩展了该函数
// 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 {
...
}
通过
// 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,
}
因此,
// https://github.com/cdot-network/ibc-demo/blob/master/cli/src/main.rs
client
.test_create_client(...)
可以调用 test_create_client
函数。
请参阅文档 substrate_subxt_proc_macro::Call 获取详细信息。
单元测试
在单元测试中,我们遵循 substrate 的文档 Runtime Tests。
模拟环境在 mock.rs 中构建;在 tests.rs 中,测试了托盘的可调用函数。
源代码中的实现逻辑
同步其他链的区块头
- 中继通过调用
Datagram::ClientUpdate
挂钩将其他链的最新区块头发送到 ibc 托盘
// 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
- 如果经过验证,则将传入的区块头的 commitment_root 和区块高度插入到存储
ConsensusStates
中。
// https://github.com/cdot-network/substrate-ibc/blob/master/src/lib.rs
ConsensusStates::insert((identifier, header.height), new_consensus_state);
连接打开握手 - ICS-003
如 Opening Handshake 中的表格所示,两个链(A & B)之间的握手包含 4 个步骤。
发起者 | 数据报 | 受影响的链 | 初始状态(A,B) | 后续状态(A,B) |
---|---|---|---|---|
行为者 | ConnOpenInit |
A | (none, none) | (INIT, none) |
中继 | ConnOpenTry |
B | (INIT, none) | (INIT, TRYOPEN) |
中继 | ConnOpenAck |
A | (INIT, TRYOPEN) | (OPEN, TRYOPEN) |
中继 | ConnOpenConfirm |
B | (OPEN, TRYOPEN) | (OPEN, OPEN) |
(none, none) -> (INIT, none)
这是通过一个行为者完成的,该行为者在链 A 中调用函数 conn_open_init
。
// 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 {
...
}
(INIT, none) -> (INIT, TRYOPEN)
中继检测到链 A 连接的 INIT
状态,然后通过调用链 B 的函数 pub fn handle_datagram(datagram: Datagram)
尝试将链 B 的连接状态设置为 TRYOPEN
,其中 Datagram::ConnOpenTry
将被匹配。
// https://github.com/cdot-network/substrate-ibc/blob/master/src/lib.rs
pub fn handle_datagram(datagram: Datagram) -> dispatch::DispatchResult {
match datagram {
...
Datagram::ConnOpenTry {
...
}
(INIT, TRYOPEN) -> (OPEN, TRYOPEN)
中继检测到链 B 连接的 TRYOPEN
,然后通过调用链 A 的函数 pub fn handle_datagram(datagram: Datagram)
尝试将链 A 的连接状态设置为 OPEN
,其中 Datagram::ConnOpenAck
将被匹配。
// https://github.com/cdot-network/substrate-ibc/blob/master/src/lib.rs
pub fn handle_datagram(datagram: Datagram) -> dispatch::DispatchResult {
match datagram {
...
Datagram::ConnOpenAck {
...
}
(OPEN, TRYOPEN) -> (OPEN, OPEN)
中继器检测到链A连接的OPEN
状态,然后尝试通过调用链B的函数pub fn handle_datagram(datagram: Datagram)
将链B连接的状态设置为OPEN
,其分支Datagram::ConnOpenConfirm
将被匹配。
// https://github.com/cdot-network/substrate-ibc/blob/master/src/lib.rs
pub fn handle_datagram(datagram: Datagram) -> dispatch::DispatchResult {
match datagram {
...
Datagram::ConnOpenConfirm {
...
}
通道开启握手 - ICS-004
在两条链(A和B)完成连接握手后,它们能够通过连接握手建立通道。
根据通道生命周期管理中的表格,两条链(A和B)之间的握手包括4个步骤。
发起者 | 数据报 | 受影响的链 | 初始状态(A,B) | 后续状态(A,B) |
---|---|---|---|---|
行为者 | ChanOpenInit | A | (none, none) | (INIT, none) |
中继 | ChanOpenTry | B | (INIT, none) | (INIT, TRYOPEN) |
中继 | ChanOpenAck | A | (INIT, TRYOPEN) | (OPEN, TRYOPEN) |
中继 | ChanOpenConfirm | B | (OPEN, TRYOPEN) | (OPEN, OPEN) |
(none, none) -> (INIT, none)
这由一个操作者完成,该操作者在链A中调用函数chan_open_init
。
// https://github.com/cdot-network/substrate-ibc/blob/master/src/lib.rs
pub fn chan_open_init(
...
) -> dispatch::DispatchResult {
...
}
(INIT, none) -> (INIT, TRYOPEN)
中继器检测到链A通道的INIT
状态,然后尝试通过调用链B的函数pub fn handle_datagram(datagram: Datagram)
将链B通道的状态设置为TRYOPEN
,其分支Datagram::ChanOpenTry
将被匹配。
// https://github.com/cdot-network/substrate-ibc/blob/master/src/lib.rs
pub fn handle_datagram(datagram: Datagram) -> dispatch::DispatchResult {
match datagram {
...
Datagram::ChanOpenTry {
...
}
(INIT, TRYOPEN) -> (OPEN, TRYOPEN)
中继器检测到链B通道的TRYOPEN
状态,然后尝试通过调用链A的函数pub fn handle_datagram(datagram: Datagram)
将链A通道的状态设置为OPEN
,其分支Datagram::ChanOpenAck
将被匹配。
// https://github.com/cdot-network/substrate-ibc/blob/master/src/lib.rs
pub fn handle_datagram(datagram: Datagram) -> dispatch::DispatchResult {
match datagram {
...
Datagram::ChanOpenAck {
...
}
(OPEN, TRYOPEN) -> (OPEN, OPEN)
中继器检测到链A通道的OPEN
状态,然后尝试通过调用链B的函数pub fn handle_datagram(datagram: Datagram)
将链B通道的状态设置为OPEN
,其分支Datagram::ChanOpenConfirm
将被匹配。
// https://github.com/cdot-network/substrate-ibc/blob/master/src/lib.rs
pub fn handle_datagram(datagram: Datagram) -> dispatch::DispatchResult {
match datagram {
...
Datagram::ChanOpenConfirm {
...
}
数据包流与处理 - ICS-004
在两条链(A和B)完成通道握手后,它们能够在通道上相互发送数据包。
根据数据包流与处理中的流程图,从链A向链B发送数据包的标准流程包括3个步骤。
发送数据包
链A的ibc pallet中的可调用函数send_packet
通过记录一个RawEvent::SendPacket
事件来发送数据包。
// 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(
...
));
}
接收数据包并写入确认
中继器检测到链A的RawEvent::SendPacket
事件,然后尝试调用链B的函数pub fn handle_datagram(datagram: Datagram)
,并匹配其分支Datagram::PacketRecv
,以便链B接收数据包。
在接收到数据包后,链B将事件RawEvent::RecvPacket
作为确认。
// 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(
...
));
}
处理确认
中继器检测到链B的RawEvent::RecvPacket
事件,然后尝试调用链A的函数pub fn handle_datagram(datagram: Datagram)
,并匹配其分支Datagram::PacketAcknowledgement
,以便链A处理确认。
// https://github.com/cdot-network/substrate-ibc/blob/master/src/lib.rs
pub fn handle_datagram(datagram: Datagram) -> dispatch::DispatchResult {
match datagram {
...
Datagram::PacketAcknowledgement {
...
}
参考文档
您可以通过运行以下命令查看此组件的参考文档:
cargo doc --open
或访问此网站:https://docs.rs/pallet-ibc
依赖项
~8–18MB
~242K SLoC