Rust跨链消息模拟库xcm-simulator的使用:XCM协议测试与多链交互模拟工具

Rust跨链消息模拟库xcm-simulator的使用:XCM协议测试与多链交互模拟工具

安装

在项目目录中运行以下Cargo命令:

cargo add xcm-simulator

或者在Cargo.toml中添加以下行:

xcm-simulator = "22.0.0"

示例代码

以下是使用xcm-simulator进行XCM协议测试和多链交互模拟的基本示例:

use xcm_simulator::{TestExt, XcmExecutor};
use xcm::latest::prelude::*;

// 定义两个测试链
pub struct TestNetwork;
impl TestExt for TestNetwork {
    fn new_ext() -> sp_io::TestExternalities {
        let mut t = frame_system::GenesisConfig::default()
            .build_storage::<TestRuntime>()
            .unwrap();
        let mut ext = sp_io::TestExternalities::new(t);
        ext.execute_with(|| System::set_block_number(1));
        ext
    }
}

// 定义XCM执行器
pub type LocalOriginToLocation = (
    // 将本地Origin转换为XCM位置
    SovereignSignedViaLocation<ParentLocation, Origin>,
);

pub type XcmRouter = (
    // 路由到父链
    XcmPassthrough<ParentIsPreset<ParentLocation>>,
);

// 测试XCM消息发送
#[test]
fn send_xcm_message() {
    TestNetwork::new_ext().execute_with(|| {
        // 准备XCM消息
        let message = Xcm(vec![
            WithdrawAsset((Parent, 100u128).into()),
            BuyExecution {
                fees: (Parent, 1u128).into(),
                weight_limit: Limited(100u64),
            },
            DepositAsset {
                assets: All.into(),
                beneficiary: Parent.into(),
            },
        ]);

        // 发送XCM消息
        XcmExecutor::<XcmConfig>::send_xcm(Parent, message).unwrap();

        // 验证消息是否被正确处理
        assert_eq!(XcmExecutor::<XcmConfig>::sent_xcm(), vec![(Parent, message)]);
    });
}

主要功能

  1. 多链环境模拟:可以模拟多个区块链之间的交互
  2. XCM消息测试:测试跨链消息的发送、接收和处理
  3. 资产转移验证:验证跨链资产转移的正确性
  4. 执行跟踪:跟踪XCM消息的执行过程和结果

高级用法示例

use xcm_simulator::{decl_test_network, decl_test_parachain, decl_test_relay_chain};

// 定义测试中继链
decl_test_relay_chain! {
    pub struct Relay {
        Runtime = relay_chain::Runtime,
        XcmConfig = relay_chain::XcmConfig,
        new_ext = relay_chain::new_test_ext(),
    }
}

// 定义测试平行链
decl_test_parachain! {
    pub struct ParaA {
        Runtime = para_a::Runtime,
        XcmConfig = para_a::XcmConfig,
        new_ext = para_a::new_test_ext(),
    }
}

// 定义测试网络
decl_test_network! {
    pub struct TestNet {
        relay_chain = Relay,
        parachains = vec![
            (1000, ParaA),
        ],
    }
}

#[test]
fn test_asset_transfer() {
    TestNet::reset();
    
    // 在中继链上初始化资产
    Relay::execute_with(|| {
        // 初始化账户余额
        let _ = relay_chain::Balances::make_free_balance_be(
            &para_a::AccountId::from([1u8; 32]),
            1000
        );
    });

    // 在平行链上发送XCM消息
    ParaA::execute_with(|| {
        let message = Xcm(vec![
            WithdrawAsset((Parent, 500u128).into()),
            BuyExecution {
                fees: (Parent, 1u128).into(),
                weight_limit: Limited(100u64),
            },
            DepositAsset {
                assets: All.into(),
                beneficiary: Parent.into(),
            },
        ]);

        XcmExecutor::<para_a::XcmConfig>::send_xcm(Parent, message).unwrap();
    });

    // 验证中继链上的余额变化
    Relay::execute_with(|| {
        assert_eq!(
            relay_chain::Balances::free_balance(&para_a::AccountId::from([1u8; 32])),
            500
        );
    });
}

完整示例demo

以下是一个更完整的xcm-simulator使用示例,模拟了中继链和平行链之间的资产转移:

// 引入必要的库和模块
use xcm_simulator::{decl_test_network, decl_test_parachain, decl_test_relay_chain};
use xcm::latest::prelude::*;
use frame_support::{assert_ok, traits::Currency};

// 定义中继链Runtime模拟
mod relay_chain {
    use super::*;
    
    // 中继链Runtime配置
    pub struct Runtime;
    pub struct XcmConfig;
    
    // 余额模块类型
    pub type Balance = u128;
    pub type AccountId = [u8; 32];
    
    // 模拟余额存储
    pub struct Balances;
    impl Currency<AccountId> for Balances {
        type Balance = Balance;
        
        fn free_balance(who: &AccountId) -> Balance {
            // 实际项目中应从存储中读取
            0
        }
        
        fn make_free_balance_be(who: &AccountId, amount: Balance) {
            // 实际项目中应写入存储
        }
    }
    
    // 创建测试环境
    pub fn new_test_ext() -> sp_io::TestExternalities {
        sp_io::TestExternalities::new(Default::default())
    }
}

// 定义平行链Runtime模拟
mod para_a {
    use super::*;
    
    // 平行链Runtime配置
    pub struct Runtime;
    pub struct XcmConfig;
    
    // 创建测试环境
    pub fn new_test_ext() -> sp_io::TestExternalities {
        sp_io::TestExternalities::new(Default::default())
    }
}

// 定义测试中继链
decl_test_relay_chain! {
    pub struct Relay {
        Runtime = relay_chain::Runtime,
        XcmConfig = relay_chain::XcmConfig,
        new_ext = relay_chain::new_test_ext(),
    }
}

// 定义测试平行链
decl_test_parachain! {
    pub struct ParaA {
        Runtime = para_a::Runtime,
        XcmConfig = para_a::XcmConfig,
        new_ext = para_a::new_test_ext(),
    }
}

// 定义测试网络
decl_test_network! {
    pub struct TestNet {
        relay_chain = Relay,
        parachains = vec![
            (1000, ParaA),
        ],
    }
}

#[test]
fn test_complete_asset_transfer() {
    // 重置测试网络
    TestNet::reset();
    
    // 初始化账户
    let alice = [1u8; 32];
    let initial_balance = 1000u128;
    let transfer_amount = 500u128;
    
    // 在中继链上初始化Alice的余额
    Relay::execute_with(|| {
        relay_chain::Balances::make_free_balance_be(&alice, initial_balance);
    });

    // 验证初始余额
    Relay::execute_with(|| {
        assert_eq!(
            relay_chain::Balances::free_balance(&alice),
            initial_balance
        );
    });

    // 在平行链上发送XCM消息进行资产转移
    ParaA::execute_with(|| {
        let message = Xcm(vec![
            // 从Alice账户提取资产
            WithdrawAsset((Parent, transfer_amount).into()),
            // 支付执行费用
            BuyExecution {
                fees: (Parent, 1u128).into(),
                weight_limit: Limited(100u64),
            },
            // 将资产存入中继链的Alice账户
            DepositAsset {
                assets: All.into(),
                beneficiary: Junction::AccountId32 {
                    id: alice,
                    network: None,
                }.into(),
            },
        ]);

        // 发送XCM消息到中继链
        assert_ok!(XcmExecutor::<para_a::XcmConfig>::send_xcm(Parent, message));
    });

    // 验证中继链上Alice的余额变化
    Relay::execute_with(|| {
        assert_eq!(
            relay_chain::Balances::free_balance(&alice),
            initial_balance - transfer_amount
        );
    });
}

注意事项

  1. 确保使用最新版本的xcm-simulator以获得最佳兼容性
  2. 测试前需要正确配置各链的XCM参数
  3. 复杂的XCM消息可能需要调整权重限制
  4. 测试网络环境需要先初始化再使用

xcm-simulator是Polkadot生态中测试跨链交互的强大工具,特别适合开发需要多链交互的Substrate应用程序。


1 回复

Rust跨链消息模拟库xcm-simulator的使用:XCM协议测试与多链交互模拟工具

概述

xcm-simulator是一个用于测试和模拟跨共识消息(XCM)格式的Rust库,它允许开发者在本地环境中模拟多链交互场景,而无需部署实际的区块链网络。这个工具特别适合Substrate和Polkadot生态系统的开发者,用于测试XCM消息的发送、接收和处理逻辑。

主要特性

  • 提供模拟的链环境(中继链、平行链等)
  • 支持XCM消息的发送和接收模拟
  • 允许自定义链的配置和参数
  • 提供断言工具验证XCM执行结果
  • 支持测试跨链资产转移等常见场景

使用方法

添加依赖

首先,在Cargo.toml中添加依赖:

[dependencies]
xcm-simulator = "0.1"

基本示例

use xcm_simulator::{TestExt, PolkadotXcm, RelayChain, Parachain};

fn main() {
    // 初始化模拟环境
    MockNet::reset();
    
    // 创建中继链和平行链
    let relay_chain = RelayChain::new();
    let para_1 = Parachain::new(1);
    let para_2 = Parachain::new(2);
    
    // 注册平行链到中继链
    relay_chain.register_parachain(1, para_1.clone());
    relay_chain.register_parachain(2, para极链2.clone());
    
    // 发送XCM消息从平行链1到平行链2
    para_1.execute_with(|| {
        let message = Xcm::<()>::new()
            .withdraw_asset((Here, 100u128).into())
            .buy_execution((Here, 1u128).into())
            .deposit_asset(All.into(), (Parachain(2), Junction::AccountId32 { id: [0u8; 32], network: None }).into());
            
        PolkadotXcm::send_xcm(Here, (Parent, Parachain(2)), message).unwrap();
    });
    
    // 验证消息处理结果
    para_2.execute_with(|| {
        assert_eq!(Balances::free_balance(&AccountId::new([0u8; 32])), 100);
    });
}

测试XCM指令

use xcm::latest::prelude::*;

#[test]
fn test_xcm_transfer() {
    MockNet::reset();
    
    let relay = RelayChain::new();
    let para_a = Parachain::new(1);
    let para_b = Parachain::new(2);
    
    relay.register_parachain(1, para_a.clone());
    relay.register_parachain(2, para_b.clone());
    
    // 在平行链A上初始化一些资产
    para_a.execute_with(|| {
        Balances::set_balance(&ALICE, 1000);
    });
    
    // 发送XCM转账消息
    para_a.execute_with(|| {
        let message = Xcm(vec![
            WithdrawAsset((Here, 100).into()),
            BuyExecution { 
                fees: (Here, 1).into(),
                weight_limit: Unlimited,
            },
            DepositAsset {
                assets: All.into(),
                beneficiary: (Parent, Parachain(2), Junction::AccountId32 { id: BOB, network: None }).into(),
            },
        ]);
        
        assert_ok!(XcmPallet::send_xcm(Here, (Parent, Parachain极链(2)), message));
    });
    
    // 验证平行链B收到资产
    para_b.execute_with(|| {
        assert_eq!(Balances::free_balance(&BOB), 99); // 100减去1的执行费用
    });
}

自定义链配置

use xcm_simulator::{decl_test_network, decl_test_parachain, decl_test_relay_chain};

// 定义自定义测试网络
decl_test_network! {
    pub struct MyTestNet {
        relay_chain = MyRelayChain,
        parachains = vec![
            (1, ParaA),
            (2, ParaB),
        ],
    }
}

// 定义中继链
decl_test_relay_chain! {
    pub struct MyRelayChain {
        Runtime = relay_chain::Runtime,
        XcmpMessageHandler = relay_chain::XcmpQueue,
        DmpMessageHandler = relay_chain::DmpQueue,
    }
}

// 定义平行链A
decl_test_parachain! {
    pub struct ParaA {
        Runtime = para_a::Runtime,
        XcmpMessageHandler = para_a::XcmpQueue,
        DmpMessageHandler = para_a::DmpQueue,
    }
}

// 定义平行链B
decl_test_parachain! {
    pub struct ParaB {
        Runtime = para_b::Runtime,
        XcmpMessageHandler = para_b::XcmpQueue,
        DmpMessageHandler = para_b::DmpQueue,
    }
}

#[test]
fn test_custom_network() {
    MyTestNet::reset();
    
    MyTestNet::relay().execute_with(|| {
        // 中继链初始化逻辑
    });
    
    MyTestNet::parachain(1).execute_with(|| {
        // 平行链A测试逻辑
    });
}

高级用法

测试XCM错误处理

#[test]
fn test_xcm_error_handling() {
    MockNet::reset();
    
    let relay = RelayChain::new();
    let para = Parachain::new(1);
    
    relay.register_parachain(1, para.clone());
    
    // 发送会失败的XCM消息
    para.execute_with(|| {
        let message = Xcm(vec![
            // 没有WithdrawAsset指令直接尝试BuyExecution
            BuyExecution {
                fees: (Here, 1).into(),
                weight_limit: Unlimited,
            },
        ]);
        
        assert_err!(
            XcmPallet::send_xcm(Here, Parent, message),
            "Expected error when sending invalid XCM"
        );
    });
}

测试跨链资产转移

#[test]
fn test_cross_chain_asset_transfer() {
    MockNet::reset();
    
    let relay = RelayChain::new();
    let para_a = Parachain::new(1);
    let para_b = Parachain::new(2);
    
    relay.register_parachain(1, para_a.clone());
    relay.register_parachain(2, para_b.clone());
    
    // 在平行链A上注册资产
    para_a.execute_with(|| {
        Assets::create(&ALICE, 1000).unwrap();
        Assets::mint(&ALICE, &ALICE, 100极链0).unwrap();
    });
    
    // 发送资产到平行链B
    para_a.execute_with(|| {
        let message = Xcm(vec![
            WithdrawAsset((X1(GeneralIndex(0)), 100).into()),
            BuyExecution {
                fees: (X1(GeneralIndex(0)), 10).into(),
                weight_limit: Unlimited,
            },
            DepositAsset {
                assets: (X1(GeneralIndex(0)), 90).into(),
                beneficiary: (Parent, Parachain(2), Junction::AccountId32 { id: BOB, network: None }).into(),
            },
        ]);
        
        assert_ok!(XcmPallet::send_xcm(Here, (Parent, Parachain(2)), message));
    });
    
    // 验证平行链B收到资产
    para_b.execute_with(|| {
        assert_eq!(Assets::balance(0, &BOB), 90);
    });
}

完整示例

下面是一个完整的跨链资产转移测试示例,结合了多个功能点:

//! 完整的XCM模拟测试示例:跨链资产转移

use xcm_simulator::{TestExt, RelayChain, Parachain};
use xcm::latest::prelude::*;
use frame_support::assert_ok;

// 定义测试账户
const ALICE: [u8; 32] = [1u8; 32];
const BOB: [u8; 32] = [2u8; 32];

#[test]
fn comprehensive_xcm_asset_transfer() {
    // 1. 初始化模拟网络
    MockNet::reset();
    
    // 2. 创建中继链和平行链
    let relay = RelayChain::new();
    let para_a = Parachain::new(1);
    let para_b = Parachain::new(2);
    
    // 3. 注册平行链到中继链
    relay.register_parachain(1, para_a.clone());
    relay.register_parachain(2, para_b.clone());
    
    // 4. 在平行链A上初始化资产和账户
    para_a.execute_with(|| {
        // 创建资产
        assert_ok!(Assets::create(Origin::signed(ALICE), 0, ALICE, 1));
        // 给ALICE账户铸造1000个资产
        assert_ok!(Assets::mint(Origin::signed(ALICE), 0, ALICE, 1000));
    });
    
    // 5. 发送XCM消息从平行链A到平行链B
    para_a.execute_with(|| {
        let message = Xcm(vec![
            // 提取100个资产
            WithdrawAsset((X1(GeneralIndex(0)), 100).into()),
            // 购买执行时间,支付10个资产作为费用
            BuyExecution {
                fees: (X1(GeneralIndex(0)), 10).into(),
                weight_limit: Unlimited,
            },
            // 将剩余90个资产存入BOB账户
            DepositAsset {
                assets: (X1(GeneralIndex(0)), 90).into(),
                beneficiary: (Parent, Parachain(2), Junction::AccountId32 { id: BOB, network: None }).into(),
            },
        ]);
        
        // 发送XCM消息
        assert_ok!(XcmPallet::send_xcm(Here, (Parent, Parachain(2)), message));
    });
    
    // 6. 验证平行链B上的资产接收情况
    para_b.execute_with(|| {
        // 检查BOB账户是否收到90个资产
        assert_eq!(Assets::balance(0, &BOB), 90);
        
        // 检查是否有正确的事件发出
        let expected_event = Event::XcmPallet(crate::Event::AssetsTransferred {
            sender: ALICE,
            recipient: BOB,
            amount: 90,
            asset_id: 0,
        });
        assert!(System::events().iter().any(|r| r.event == expected_event));
    });
    
    // 7. 验证平行链A上的资产扣除情况
    para_a.execute_with(|| {
        // ALICE的资产应该减少100 (1000 - 100 = 900)
        assert_eq!(Assets::balance(0, &ALICE), 900);
    });
}

注意事项

  1. xcm-simulator主要用于测试环境,不应在生产环境中使用
  2. 模拟的链环境是简化的,可能与实际网络行为有差异
  3. 测试时应覆盖各种边界条件和错误场景
  4. 确保XCM消息的weight计算准确,避免实际部署时出现执行失败

通过xcm-simulator,开发者可以高效地测试和验证XCM消息的逻辑,确保跨链交互的正确性和可靠性。

回到顶部