Skip to content

Testing and Debugging

Introduction

Cross-Consensus Messaging (XCM) is a core feature of the Polkadot ecosystem, enabling communication between parachains, relay chains, and system chains. To ensure the reliability of XCM-powered blockchains, thorough testing and debugging are essential before production deployment.

This guide covers the XCM Emulator, a tool designed to facilitate onboarding and testing for developers. Use the emulator if:

  • A live runtime is not yet available
  • Extensive configuration adjustments are needed, as emulated chains differ from live networks
  • Rust-based tests are preferred for automation and integration

For scenarios where real blockchain state is required, Chopsticks allows testing with any client compatible with Polkadot SDK-based chains.

XCM Emulator

Setting up a live network with multiple interconnected parachains for XCM testing can be complex and resource-intensive.

The xcm-emulator is a tool designed to simulate the execution of XCM programs using predefined runtime configurations. These configurations include those utilized by live networks like Kusama, Polkadot, and Asset Hub.

This tool enables testing of cross-chain message passing, providing a way to verify outcomes, weights, and side effects efficiently. It achieves this by utilizing mocked runtimes for both the relay chain and connected parachains, enabling developers to focus on message logic and configuration without needing a live network.

The xcm-emulator relies on transport layer pallets. However, the messages do not leverage the same messaging infrastructure as live networks since the transport mechanism is mocked. Additionally, consensus-related events are not covered, such as disputes and staking events. Parachains should use end-to-end (E2E) tests to validate these events.

Advantages and Limitations

The XCM Emulator provides both advantages and limitations when testing cross-chain communication in simulated environments.

  • Advantages:

    • Interactive debugging - offers tracing capabilities similar to EVM, enabling detailed analysis of issues
    • Runtime composability - facilitates testing and integration of multiple runtime components
    • Immediate feedback - supports Test-Driven Development (TDD) by providing rapid test results
    • Seamless integration testing - simplifies the process of testing new runtime versions in an isolated environment
  • Limitations:

    • Simplified emulation - always assumes message delivery, which may not mimic real-world network behavior
    • Dependency challenges - requires careful management of dependency versions and patching. Refer to the Cargo dependency documentation
    • Compilation overhead - testing environments can be resource-intensive, requiring frequent compilation updates

How Does It Work?

The xcm-emulator provides macros for defining a mocked testing environment. Check all the existing macros and functionality in the XCM Emulator source code. The most important macros are:

  • decl_test_relay_chains - defines runtime and configuration for the relay chains. Example:

    // Westend declaration
    decl_test_relay_chains! {
        #[api_version(11)]
        pub struct Westend {
            genesis = genesis::genesis(),
            on_init = (),
            runtime = westend_runtime,
            core = {
                SovereignAccountOf: westend_runtime::xcm_config::LocationConverter,
            },
            pallets = {
                XcmPallet: westend_runtime::XcmPallet,
                Sudo: westend_runtime::Sudo,
                Balances: westend_runtime::Balances,
                Treasury: westend_runtime::Treasury,
                AssetRate: westend_runtime::AssetRate,
                Hrmp: westend_runtime::Hrmp,
                Identity: westend_runtime::Identity,
                IdentityMigrator: westend_runtime::IdentityMigrator,
            }
        },
    }
    
  • decl_test_parachains - defines runtime and configuration for the parachains. Example:

    // AssetHubWestend Parachain declaration
    decl_test_parachains! {
        pub struct AssetHubWestend {
            genesis = genesis::genesis(),
            on_init = {
                asset_hub_westend_runtime::AuraExt::on_initialize(1);
            },
            runtime = asset_hub_westend_runtime,
            core = {
                XcmpMessageHandler: asset_hub_westend_runtime::XcmpQueue,
                LocationToAccountId: asset_hub_westend_runtime::xcm_config::LocationToAccountId,
                ParachainInfo: asset_hub_westend_runtime::ParachainInfo,
                MessageOrigin: cumulus_primitives_core::AggregateMessageOrigin,
            },
            pallets = {
                PolkadotXcm: asset_hub_westend_runtime::PolkadotXcm,
                Balances: asset_hub_westend_runtime::Balances,
                Assets: asset_hub_westend_runtime::Assets,
                ForeignAssets: asset_hub_westend_runtime::ForeignAssets,
                PoolAssets: asset_hub_westend_runtime::PoolAssets,
                AssetConversion: asset_hub_westend_runtime::AssetConversion,
            }
        },
    }
    
  • decl_test_bridges - creates bridges between chains, specifying the source, target, and message handler. Example:

    decl_test_bridges! {
        pub struct RococoWestendMockBridge {
            source = BridgeHubRococoPara,
            target = BridgeHubWestendPara,
            handler = RococoWestendMessageHandler
        },
        pub struct WestendRococoMockBridge {
            source = BridgeHubWestendPara,
            target = BridgeHubRococoPara,
            handler = WestendRococoMessageHandler
        }
    }
    
  • decl_test_networks - defines a testing network with relay chains, parachains, and bridges, implementing message transport and processing logic. Example:

    decl_test_networks! {
        pub struct WestendMockNet {
            relay_chain = Westend,
            parachains = vec![
                AssetHubWestend,
                BridgeHubWestend,
                CollectivesWestend,
                CoretimeWestend,
                PeopleWestend,
                PenpalA,
                PenpalB,
            ],
            bridge = ()
        },
    }
    

By leveraging these macros, developers can customize their testing networks by defining relay chains and parachains tailored to their needs. For guidance on implementing a mock runtime for a Polkadot SDK-based chain, refer to the Pallet Testing article.

This framework enables thorough testing of runtime and cross-chain interactions, enabling developers to effectively design, test, and optimize cross-chain functionality.

To see a complete example of implementing and executing tests, refer to the integration tests in the Polkadot SDK repository.

Last update: March 12, 2025
| Created: October 18, 2024