Rust跨链消息传递库xcm-runtime-apis的使用:实现Substrate区块链XCM(Cross-Consensus Messaging)运行时API集成
Rust跨链消息传递库xcm-runtime-apis的使用:实现Substrate区块链XCM(Cross-Consensus Messaging)运行时API集成
安装
在项目目录中运行以下Cargo命令:
cargo add xcm-runtime-apis
或者在Cargo.toml中添加以下行:
xcm-runtime-apis = "0.9.0"
使用示例
下面是一个完整的示例,展示如何使用xcm-runtime-apis库实现Substrate区块链的XCM运行时API集成:
// 引入必要的库和模块
use frame_support::{
traits::{Get, OriginTrait},
weights::Weight,
};
use xcm::latest::prelude::*;
use xcm_runtime_apis::{
self as api,
xcm::{ExecuteXcm, SendXcm},
};
// 实现XCM执行API
pub struct XcmExecuteApi;
impl ExecuteXcm for XcmExecuteApi {
type Call = super::RuntimeCall;
fn execute_xcm(
origin: <Self::Origin as OriginTrait>::PalletsOrigin,
message: Xcm<()>,
max_weight: Weight,
) -> api::xcm::ExecuteXcmResult {
// 在这里实现XCM消息的执行逻辑
// 例如:验证origin、解析message、执行跨链操作等
Ok(())
}
fn execute_xcm_in_credit(
origin: <Self::Origin as OriginTrait>::PalletsOrigin,
message: Xcm<()>,
max_weight: Weight,
weight_credit: Weight,
) -> api::xcm::ExecuteXcmResult {
// 带权重信用的执行版本
Ok(())
}
}
// 实现XCM发送API
pub struct XcmSendApi;
impl SendXcm for XcmSendApi {
fn send_xcm(
destination: impl Into<MultiLocation>,
message: Xcm<()>,
) -> api::xcm::SendXcmResult {
// 在这里实现XCM消息的发送逻辑
// 例如:验证destination、准备消息、发送到目标链等
Ok(())
}
}
// 在Runtime中集成XCM API
impl api::xcm::XcmApi for Runtime {
type ExecuteXcm = XcmExecuteApi;
type SendXcm = XcmSendApi;
}
// 示例Runtime配置
pub struct Runtime;
impl frame_system::Config for Runtime {
type RuntimeCall = RuntimeCall;
type RuntimeEvent = RuntimeEvent;
type RuntimeOrigin = RuntimeOrigin;
// 其他必要的关联类型
}
// 定义RuntimeCall和RuntimeEvent等类型
pub enum RuntimeCall {}
pub enum RuntimeEvent {}
pub struct RuntimeOrigin;
API说明
xcm-runtime-apis提供了以下关键API:
-
ExecuteXcm - 处理接收到的XCM消息的执行
execute_xcm()
- 执行XCM消息execute_xcm_in_credit()
- 带权重信用的执行版本
-
SendXcm - 处理XCM消息的发送
send_xcm()
- 发送XCM消息到目标链
-
XcmApi - 运行时API的主要接口,组合了执行和发送功能
实现注意事项
- 需要为你的Runtime实现
frame_system::Config
和其他必要的trait - 在
ExecuteXcm
和SendXcm
实现中处理错误情况和边界条件 - 考虑权重计算和资源限制
- 确保消息验证和安全性
完整示例代码
// 引入必要的库和模块
use frame_support::{
traits::{Get, OriginTrait},
weights::Weight,
};
use sp_std::marker::PhantomData;
use xcm::latest::prelude::*;
use xcm_runtime_apis::{
self as api,
xcm::{ExecuteXcm, SendXcm},
};
// 定义Runtime类型
pub struct Runtime;
// 实现frame_system::Config
impl frame_system::Config for Runtime {
type RuntimeCall = RuntimeCall;
type RuntimeEvent = RuntimeEvent;
type RuntimeOrigin = RuntimeOrigin;
type Block = Block;
type AccountId = u64;
type BlockHashCount = frame_support::traits::ConstU64<250>;
type DbWeight = ();
type BaseCallFilter = frame_support::traits::Everything;
type Version = ();
type PalletInfo = PalletInfo;
type OnNewAccount = ();
type OnKilledAccount = ();
type SystemWeightInfo = ();
type SS58Prefix = ();
type OnSetCode = ();
type MaxConsumers = frame_support::traits::ConstU32<16>;
}
// 定义RuntimeCall类型
pub enum RuntimeCall {
System(SystemCall),
Xcm(XcmCall),
}
// 定义SystemCall
pub enum SystemCall {
remark { remark: Vec<u8> },
}
// 定义XcmCall
pub enum XcmCall {
send { dest: MultiLocation, message: Xcm<()> },
}
// 定义RuntimeEvent类型
pub enum RuntimeEvent {
System(SystemEvent),
Xcm(XcmEvent),
}
// 定义SystemEvent
pub enum SystemEvent {
Remarked { who: u64, hash: [u8; 32] },
}
// 定义XcmEvent
pub enum XcmEvent {
Sent { message_id: [u8; 32] },
}
// 定义RuntimeOrigin类型
pub struct RuntimeOrigin;
// 实现OriginTrait for RuntimeOrigin
impl OriginTrait for RuntimeOrigin {
type PalletsOrigin = PalletsOrigin;
fn add_filter(&mut self, _filter: impl Fn(&Self::PalletsOrigin) -> bool + 'static) {
unimplemented!()
}
fn reset_filter(&mut self) {
unimplemented!()
}
}
// 定义PalletsOrigin类型
pub enum PalletsOrigin {
System,
Xcm,
}
// 实现XCM执行API
pub struct XcmExecuteApi;
impl ExecuteXcm for XcmExecuteApi {
type Call = RuntimeCall;
fn execute_xcm(
origin: <Self::Origin as OriginTrait>::PalletsOrigin,
message: Xcm<()>,
max_weight: Weight,
) -> api::xcm::ExecuteXcmResult {
log::info!("Executing XCM message: {:?}", message);
// 这里应该添加实际的执行逻辑
Ok(())
}
fn execute_xcm_in_credit(
origin: <Self::Origin as OriginTrait>::PalletsOrigin,
message: Xcm<()>,
max_weight: Weight,
weight_credit: Weight,
) -> api::xcm::ExecuteXcmResult {
log::info!("Executing XCM message with weight credit: {:?}", message);
// 这里应该添加实际的执行逻辑
Ok(())
}
}
// 实现XCM发送API
pub struct XcmSendApi;
impl SendXcm for XcmSendApi {
fn send_xcm(
destination: impl Into<MultiLocation>,
message: Xcm<()>,
) -> api::xcm::SendXcmResult {
let dest = destination.into();
log::info!("Sending XCM message to {:?}: {:?}", dest, message);
// 这里应该添加实际的发送逻辑
Ok(())
}
}
// 在Runtime中集成XCM API
impl api::xcm::XcmApi for Runtime {
type ExecuteXcm = XcmExecuteApi;
type SendXcm = XcmSendApi;
}
// 其他必要的类型定义
pub struct Block;
pub struct PalletInfo;
这个完整示例扩展了原始示例,添加了更多类型定义和实现细节,包括:
- 完整的Runtime配置
- RuntimeCall和RuntimeEvent的具体实现
- 日志记录
- 更多必要的关联类型实现
- 更完整的类型系统定义
1 回复
Rust跨链消息传递库xcm-runtime-apis使用指南
完整示例Demo
以下是一个完整的Substrate链使用xcm-runtime-apis实现跨链通信的示例:
// runtime/src/lib.rs
// 1. 引入必要依赖
use frame_support::{construct_runtime, parameter_types};
use xcm::latest::prelude::*;
use xcm_builder::{
AccountId32Aliases, AllowKnownQueryResponses, AllowSubscriptionsFrom,
AllowTopLevelPaidExecutionFrom, CurrencyAdapter, FixedWeightBounds, IsConcrete,
LocationInverter, NativeAsset, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative,
SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative,
SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit,
};
use xcm_executor::{traits::*, Config as XcmConfig, XcmExecutor};
// 2. 配置XCM参数
parameter_types! {
pub const RelayLocation: MultiLocation = MultiLocation::parent();
pub const RelayNetwork: NetworkId = NetworkId::Any;
pub RelayChainOrigin: RuntimeOrigin = cumulus_pallet_xcm::Origin::Relay.into();
pub Ancestry: MultiLocation = Parachain(1000).into();
pub CheckingAccount: AccountId = XcmPallet::check_account();
}
// 3. 定义XCM配置类型
pub type LocationToAccountId = (
ParentIsPreset<AccountId>,
SiblingParachainConvertsVia<Sibling, AccountId>,
AccountId32Aliases<RelayNetwork, AccountId>,
);
pub type XcmRouter = (
// 使用UMP向下游传递消息到中继链
cumulus_primitives_utility::ParentAsUmp<ParachainSystem, (), ()>,
);
impl pallet_xcm::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
type SendXcmOrigin = EnsureXcmOrigin<RuntimeOrigin, LocalOriginToLocation>;
type XcmRouter = XcmRouter;
type ExecuteXcmOrigin = EnsureXcmOrigin<RuntimeOrigin, LocalOriginToLocation>;
type XcmExecuteFilter = Everything;
type XcmExecutor = XcmExecutor<XcmConfig>;
type XcmTeleportFilter = Nothing;
type XcmReserveTransferFilter = Everything;
type Weigher = FixedWeightBounds<UnitWeightCost, RuntimeCall, MaxInstructions>;
type UniversalLocation = UniversalLocation;
type RuntimeOrigin = RuntimeOrigin;
type RuntimeCall = RuntimeCall;
const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100;
type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion;
}
// 4. 实现XcmRuntimeApi
impl xcm_runtime_apis::XcmRuntimeApi<Block> for Runtime {
fn send_xcm(
origin: MultiLocation,
dest: MultiLocation,
message: VersionedXcm,
) -> Result<XcmHash, SendError> {
// 转换消息为最新版本
let message = match message {
VersionedXcm::V3(msg) => msg,
_ => return Err(SendError::Unroutable),
};
// 实际发送逻辑
XcmPallet::send(origin, dest, message)
}
fn execute_xcm(
origin: MultiLocation,
message: VersionedXcm,
) -> Outcome {
// 转换消息为最新版本
let message = match message {
VersionedXcm::V3(msg) => msg,
_ => return Outcome::Error(XcmError::Unimplemented),
};
// 实际执行逻辑
XcmExecutor::<XcmConfig>::execute_xcm(origin, message)
}
}
// 5. 构造runtime
construct_runtime!(
pub enum Runtime where
Block = Block,
NodeBlock = opaque::Block,
UncheckedExtrinsic = UncheckedExtrinsic
{
// 其他pallet...
XcmPallet: pallet_xcm,
}
);
// 6. 使用示例 - 发送跨链资产转移
pub fn transfer_assets_to_parachain(
dest_para_id: u32,
beneficiary: AccountId,
amount: u128,
) -> Result<(), &'static str> {
// 构建目标位置 (平行链2000)
let dest = MultiLocation::new(
1,
X1(Parachain(dest_para_id))
);
// 构建受益人位置
let beneficiary_location = MultiLocation::new(
0,
X1(AccountId32 {
network: None,
id: beneficiary.into(),
})
);
// 构建XCM消息
let message = Xcm(vec![
WithdrawAsset((Here, amount).into()),
BuyExecution {
fees: (Here, amount / 10).into(), // 支付10%作为费用
weight_limit: Limited(Weight::from_parts(1_000_000, 1024)),
},
DepositAsset {
assets: All.into(),
beneficiary: beneficiary_location,
},
]);
// 发送XCM消息
Runtime::send_xcm(Parent.into(), dest, VersionedXcm::from(message))
.map_err(|_| "Failed to send XCM message")?;
Ok(())
}
// 7. 使用示例 - 接收并处理XCM消息
pub fn handle_incoming_xcm(
origin: MultiLocation,
message: VersionedXcm,
) -> Outcome {
// 执行XCM消息
let result = Runtime::execute_xcm(origin, message);
// 处理执行结果
match result {
Outcome::Complete(weight) => {
log::info!("XCM executed successfully, weight used: {}", weight);
Outcome::Complete(weight)
},
Outcome::Incomplete(weight, error) => {
log::warn!("XCM executed partially: {:?}, weight used: {}", error, weight);
Outcome::Incomplete(weight, error)
},
Outcome::Error(error) => {
log::error!("XCM execution failed: {:?}", error);
Outcome::Error(error)
},
}
}
关键点说明
- XCM配置:定义了如何将位置转换为账户ID、资产处理等核心逻辑
- XcmRuntimeApi实现:提供了标准化的XCM消息发送和执行接口
- 资产转移示例:展示了如何构建完整的跨链资产转移流程
- 消息处理:演示了如何接收和处理传入的XCM消息
实际应用建议
- 错误处理:实际应用中需要更完善的错误处理和日志记录
- 费用估算:应根据目标链实际情况计算适当的手续费
- 版本适配:生产环境需要处理更多XCM版本兼容性问题
- 安全审计:关键XCM逻辑应进行充分的安全审计
这个完整示例展示了如何在Substrate链上集成xcm-runtime-apis实现跨链功能,包括发送和处理XCM消息的核心流程。实际使用时需要根据具体链的配置进行调整。