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 article explores two indispensable tools for XCM testing, the XCM Simulator and the XCM Emulator, to help developers onboard and test their solutions effectively.

XCM Simulator

Setting up a live network with multiple interconnected parachains for XCM testing can be complex and resource-intensive. To address this, the xcm-simulator was developed. This versatile tool enables developers to test and experiment with XCM in a controlled, simulated network environment.

The xcm-simulator offers a fast and efficient way to test XCM instructions against the xcm-executor. It serves as an experimental playground for developers, supporting features such as:

  • Mocking Downward Message Passing (DMP) - retrieve incoming XCMs from the relay chain using the received_dmp getter
  • Rapid iteration - test XCM messages in isolation without relying on full network simulations

The xcm-simulator 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.

How does it work?

The xcm-simulator provides the following macros for building a mocked simulation environment:

  • decl_test_relay_chain - implements upward message passing (UMP) for the specified relay chain struct. The struct must define the XCM configuration for the relay chain:

    decl_test_relay_chain! {
        pub struct Relay {
            Runtime = relay_chain::Runtime,
            XcmConfig = relay_chain::XcmConfig,
            new_ext = relay_ext(),
        }
    }
    

    The relay_ext() sets up a test environment for the relay chain with predefined storage, then returns a TestExternalities instance for further testing.

  • decl_test_parachain - implements the XcmMessageHandlerT and DmpMessageHandlerT traits for the specified parachain struct. Requires the parachain struct to include the XcmpMessageHandler and DmpMessageHandler pallets, which define the logic for processing messages (implemented through mock_message_queue). The patter must be the following:

    decl_test_parachain! {
        pub struct ParaA {
            Runtime = parachain::Runtime,
            XcmpMessageHandler = parachain::MsgQueue,
            DmpMessageHandler = parachain::MsgQueue,
            new_ext = para_ext(1),
        }
    }
    

    The para_ext(para_id: u32) function initializes a test environment for a parachain with a specified para_id, sets the initial configuration of the parachain, returning a TestExternalities instance for testing.

    Note

    Developers can take this idea and define as many parachains as they want, like ParaA, ParaB, ParaC, etc

  • decl_test_network - defines a testing network consisting of a relay chain and multiple parachains. Takes a network struct as input and implements functionalities for testing, including ParachainXcmRouter and RelayChainXcmRouter. The struct must specify the relay chain and an indexed list of parachains to be included in the network:

    decl_test_network! {
        pub struct ExampleNet {
            relay_chain = Relay,
            parachains = vec![
                (1, ParaA),
                (2, ParaB),
            ],
        }
    }
    

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.

For a complete example of how to use the xcm-simulator, explore the sample provided in the xcm-simulator codebase.

XCM Emulator

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 the Asset Hub.

This tool enables testing of cross-chain message passing, providing a way to verify outcomes, weights, and side effects efficiently.

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, staking, and ImOnline events. Parachains should use end-to-end (E2E) tests to validate these events.

Pros and Cons

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

  • Pros:

    • 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
  • Cons:

    • 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 package builds upon the functionality provided by the xcm-simulator package, offering the same set of macros while extending their capabilities. In addition to the standard features, xcm-emulator introduces new tools that make testing cross-chain communication more comprehensive.

One of the key additions is the decl_test_bridges macro. This macro allows developers to define and implement mock bridges for testing interoperability in the Polkadot ecosystem.

  • decl_test_bridges - enables the creation of multiple bridges between chains, specifying their source chain, target chain, and the handler responsible for processing messages

    decl_test_bridges! {
        pub struct BridgeA {
            source = ChainA,
            target = ChainB,
            handler = HandlerA
        },
        pub struct BridgeB {
            source = ChainB,
            target = ChainC,
            handler = HandlerB
        },
    }
    

Utilizing the capabilities of the xcm-emulator, developers can effectively design, test, and optimize cross-chain functionality, fostering interoperability within the Polkadot ecosystem.

Last update: December 17, 2024
| Created: October 18, 2024