logo

[Tutorial] Polkadot Cross-Chain Message Passing (XCMP) demo with ping pallet

Chris Li

Chris Li

· 8 min read
Thumbnail
Image

This article presents a demo for cross-chain function calling through Polkadot’s XCMP, with Substrate’s Ping Pallet code example.

All code mentioned in this article can be found in the OAK Network Github repo: https://github.com/AvaProtocol/ping-workshop

1. Overview

In this demo, we will leverage the polkadot v0.9.8 developed by Parity Technologies to create a relay chain network and two parachain networks, and call the cross-chain ping-pong function from parachain A to parachain B.

Specifically, we will create 4 command-line environments to host 2 validators for the relay chain network, and 1 validator for each parachain. 4 is the minimum number of nodes for this cross-chain ping-pong demonstration.

Below we explain the details.

1.1 Ping-pallet in Cumulus

Cumulus is an extension to Substrate that makes it easy to make any Substrate built runtime into a Polkadot-compatible parachain. To learn more about it, please refer to Polkadot Wiki: Cumulus.

# Custom Dependencies
cumulus-ping = { git = 'https://github.com/paritytech/cumulus', branch = 'polkadot-v0.9.8', default-features = false }

2.2 Add Cumulus-Ping into runtime code

2.2.1 Implement cumulus-ping::config in runtime

Here we refer to the code in rococo repository.

https://github.com/paritytech/cumulus/blob/ed6ba5dfb2c112c29505e856e8971da3420ce6d0/polkadot-parachains/rococo/src/lib.rs#L393

impl cumulus_ping::Config for Runtime {
  type Event = Event;
  type Origin = Origin;
  type Call = Call;
  type XcmSender = XcmRouter;
}

2.2.2 Add cumulus-ping to construct_runtime

Here we also refer to the code in rococo repository

https://github.com/paritytech/cumulus/blob/ed6ba5dfb2c112c29505e856e8971da3420ce6d0/polkadot-parachains/rococo/src/lib.rs#L459

construct_runtime! {
    pub enum Runtime where
        Block = Block,
        NodeBlock = generic::Block<Header,sp_runtime::OpaqueExtrinsic>,
        UncheckedExtrinsic = UncheckedExtrinsic,
    {
        ....
        Spambot: cumulus_ping::{Pallet, Call, Storage, Event<T>} = 99,
    }
}

And now the cumulus_ping pallet is included in the substrate-parachain-template.

3. Run relay chain + 2 parachains

3.1 Prerequisite

3.1.1 Rust environment

Run the following code to make sure you have the correct version of Rust installed.

# setup Rust nightly toolchain
rustup default nightly-2021-03-01
# setup wasm toolchain
rustup target add wasm32-unknown-unknown --toolchain nightly-2021-03-01

3.1.2 Relay chain: polkadot v0.9.8

https://github.com/paritytech/polkadot/tree/release-v0.9.8

3.1.3 Parachain:

Here we use the code base in OAK Network’s ping-workshop repo.

https://github.com/AvaProtocol/ping-workshop

The ping-workshop is based on the most recent release of substrate-parachain-template, which is compatible with Polkadot v0.9.8.

3.2 Compile and run the relay chain

3.2.1 Compile the relay chain code

git clone -b release-v0.9.8 https://github.com/paritytech/polkadot
cd polkadot
cargo build --release

3.2.2 Generate config

./target/release/polkadot build-spec --chain=rococo-local --disable-default-bootnode --raw > rococo-local.json

3.2.3 Run the code

# First node
./target/release/polkadot --name alice --chain rococo-local --alice -d ./data/alice --ws-external --rpc-external --rpc-cors all  --node-key 0000000000000000000000000000000000000000000000000000000000000001
#Second Node
./target/release/polkadot --name bob --chain rococo-local --bob -d ./data/bob --bootnodes /ip4/127.0.0.1/tcp/30333/p2p/12D3KooWEyoppNCUx8Yx66oV9fJnriXwCcXwDDUA2kj6vnc6iDEp

Check the following tutorial for details on starting a network.

​​https://substrate.dev/docs/en/tutorials/start-a-private-network/alicebob

So far we have launched two nodes and formed the relay chain network.

3.3 Compile and run the parachains

3.3.1 Compile the code

git clone https://github.com/imstar15/ping-workshop
cd ping-workshop
cargo build --release

3.3.2 Generate genesis state & wasm

We are giving the two parachain IDs 2000 and 2001. The genesis wasm file is the same for the 2 parachains, but each needs a separate genesis state file.

# Export genesis wasm
./target/release/parachain-collator export-genesis-wasm > genesis-wasm
# Export genesis-state-2000
./target/release/parachain-collator export-genesis-state --parachain-id 2000 > genesis-state-2000
# Export genesis-state-2001
./target/release/parachain-collator export-genesis-state --parachain-id 2001 > genesis-state-2001

3.3.3 Run the parachains

// First chain
RUST_LOG=runtime=debug ./target/release/parachain-collator -d ./data/alice --collator --alice --force-authoring --port 40557 --ws-port 9951 --parachain-id 2000 --ws-external --rpc-cors all --rpc-methods=unsafe -- --execution wasm --chain ../polkadot/rococo-local.json --port 40558 --bootnodes /ip4/127.0.0.1/tcp/30333/p2p/12D3KooWEyoppNCUx8Yx66oV9fJnriXwCcXwDDUA2kj6vnc6iDEp
// Second chain
RUST_LOG=runtime=debug ./target/release/parachain-collator -d ./data/bob --collator --bob --force-authoring --port 40777 --ws-port 9971 --parachain-id 2001 --ws-external --rpc-cors all --rpc-methods=unsafe -- --execution wasm --chain ../polkadot/rococo-local.json --port 40778 --bootnodes /ip4/127.0.0.1/tcp/30333/p2p/12D3KooWEyoppNCUx8Yx66oV9fJnriXwCcXwDDUA2kj6vnc6iDEp

For more details about the parachain-collator program, use the help command to check out the examples.

./target/release/parachain-collator --help

3.3.4 Connect the parachains to the relay chain

In this step, we will need to use polkadot.js/apps website to send request to the chains. Use sudo access to submit the registration information of the two parachains to araSudoWrapper.sudoScheduleParaInitialize.

As mentioned above, the para-ids are 2000 and 2001.

Navigate to polkadot.js/apps website with a local rpc target.

https://polkadot.js.org/apps/?rpc=ws%3A%2F%2F127.0.0.1%3A9944#/sudo

Image

Under the Network tab you should be able to see the 2 parachains registered on the relay chain.

Image

And here you can see the parachain’s blocks are finalized, indicating that the Parachains are successfully connected to the relay chain.

Image

4. Build HRMP channels and call the cross-chain function

XCMP-light(HRMP) is an alternative messaging channel to the XCMP which has the same interface. It’s the current implementation of a messaging channel.

4.1 Call the cross-chain function

First, let’s try to call the cross-chain function. In this case, it’s spambot.start which sends a ping message from parachain 2000 to parachain 2001

Navigate to the Developer tab of polkadot.js/apps

https://polkadot.js.org/apps/?rpc=ws%3A%2F%2F127.0.0.1%3A9951#/sudo

Execute spambot.start with below parameters,

Image

However, all we received is an error, ErrorSendingPing:

Image

After debugging we found the root cause of that error, as seen in below code.

https://github.com/paritytech/cumulus/blob/ed6ba5dfb2c112c29505e856e8971da3420ce6d0/pallets/parachain-system/src/lib.rs#L675

Image

In this get_channel_max, the number of channels is 0. Therefore, the binary_search_by_key fails to execute. The cross chain messaging channel is not yet established.

4.2 Build the HRMP channel

Now we execute paraSudoWrapper.sudoEstabllishHrmpChannel to build the messaging channel between 2000 and 2001. We also need to repeat the process for the channel from 2001 to 2000.

Image

After the channel setup, we repeat the spambot.start function in 4.1, and now we see the spambot.PingSent and spambot.Ponged events shown in recent blocks, indicating that the ping function is successfully called cross-chain.

https://polkadot.js.org/apps/?rpc=ws%3A%2F%2F127.0.0.1%3A9951#/explorer

Image

5. Analyze the code

As mentioned in 1., the code of ping function is from cumulus-ping pallet.

https://github.com/paritytech/cumulus/blob/polkadot-v0.9.8/polkadot-parachains/pallets/ping/src/lib.rs

5.1 The Start function

Use this code to start

Image

5.2 on_finalize function

The code defines a hook that when the block is finalized, the start method will send the requested message out to the destination chain with a ping message.

Image

5.3 The ping function

Image

When ping is executed, it will reply with a XCMP, calling pong function; therefore, it completes a round-trip for ping-pong.

6. Cross-chain message filtering

One important thing to note in above implementation is that a cross-chain message needs to be whitelisted in the receiver end to be accepted. The whitelisting happens in a place called the Barrier.

Without the whitelisting, the receiving chain will encounter an XcmError.Barrier error, as shown in below screenshot.

Image

Let’s take a look at the Barrier setup below.

In runtime/src/lib.rs

Image

It shows pub type Barrier = (….), where it defines 4 Barrier, and each has a message filter criteria.

https://github.com/paritytech/polkadot/blob/3a10ee63c0b5703a1c802db3438ab7e01344a8ce/xcm/xcm-builder/src/barriers.rs#L69

Image

For example, the AllowUnpaidExecutionFrom Barrier limits the origin to be within a range,

  • AllowUnpaidExecutionFrom<ParentOrParentsUnitPlurality>: only messages from the relay chain council will be executed.
  • AllowUnpaidExecutionFrom<All<MultiLocation>>: accept all messages origins
  • AllowUnpaidExecutionFrom<SpecParachain>: parachain 2000 and 2001 can only execute messages from each other
match_type! {
    pub type SpecParachain: impl Contains<MultiLocation> = {
        X2(Parent, Parachain(2000)) | X2(Parent, Parachain(2001))
    };
}

That’s all for today’s tutorial. Hope you grasp the idea of Polkadot cross-chain function calling from our article. The parachain code is based off of substrate-parachain-template and ready in the OAK Network Github:

https://github.com/AvaProtocol//ping-workshop

Stay tuned for further information about the OAK Network here on Medium, Twitter and LinkedIn, as well as in our Discord Server!

Please check out our engineering and growth job openings, contact partner@oak.tech for partnerships, or contact@oak.tech for any general inquiries.

Chris Li

About Chris Li

Chris Li is the founder and CEO of Ava Protocol. Prior to establishing Ava Protocol, Chris gained valuable experience as a Messaging Protocol Engineer at Microsoft and as a successful serial entrepreneur. He is also a long-term EVM smart contract developer, Web3 Foundation grant recipient, and Polkadot core contributor.