changed 2 years ago
Published Linked with GitHub

Setting up a local testnet using Zombienet

Zombienet is an excellenet CLI tool to spawn a local test network. With Zombienet we can spawn a local relay chain with several validators as well as parachains with multiple collators.

To see the full list of features Zombienet offers, take a look at the README:

Objectives

Setup a local Rococo testnet with the following:

  • Four validators
  • Two parachains
  • One collator per parachain
  • Create an HRMP channel between both parachains

Getting the binaries

You can use Zombienet with binaries, docker, kubernetes, and more.

In this tutorial we will setup Zombienet to use binaries.

In your root directory let's create a bin folder to hold your binaries:

mkdir bin

The first binary we will need is Polkadot. This binary will be used to spin up our relay chain.

If you're running Linux you can skip this section and download your Polkadot binary from here:

First let's clone down Polkadot:

git clone https://github.com/paritytech/polkadot

cd into the repo, checkout the latest release of Polkadot and compile:

git checkout release-v0.9.32
cargo build -release

We now have a Polkadot binary!

Copy the Polkadot binary into your bin folder:

Tip: It is good practice to append the version of polkadot to your binary name. This is a nice way of organizing in your bin folder multiple versions on your binary.

cp ./target/release/polkadot ../bin/polkadot-v0.9.32

cd back to your root directory

The next binary we will need is the substrate-parachain-template binary. We need this binary to spin up our parachains!

Let's clone down substrate-parachain-template:

git clone https://github.com/substrate-developer-hub/substrate-parachain-template

cd into the repo, checkout the latest Polkadot release, and compile:

git checkout polkadot-v0.9.32
cargo build -release

We now have a parachain collator binary.

Copy the parachain binary into your bin folder:

cp ./target/release/parachain-template-node ../bin/parachain-template-node-v0.9.32

Nice! We have all the binaries needed.

Setting up Zombienet

In this next section we will setup Zombienet and learn how to configure our testnet.

We will need to download the Zombienet executable.

You can find linux and macOS executables of the Zombienet CLI here:

Download the Zombienet CLI according to your operating system.

Tip: If you want the executable to be available system-wide then you can follow these steps (otherwise just download the executable to your working directory):

wget https://github.com/paritytech/zombienet/releases/download/v1.3.18/zombienet-macos
chmod +x zombienet-macos 
cp zombienet-macos /usr/local/bin

Let's make sure Zombienet CLI is installed correctly:

./zombienet-macos --help

You should see some similar output:

Usage: zombienet [options] [command]

Options:
  -c, --spawn-concurrency <concurrency>  Number of concurrent spawning process to launch, default is 1
  -p, --provider <provider>              Override provider to use (choices: "podman", "kubernetes", "native")
  -m, --monitor                          Start as monitor, do not auto cleanup network
  -h, --help                             display help for command

Commands:
  spawn <networkConfig> [creds]          Spawn the network defined in the config
  test <testFile> [runningNetworkSpec]   Run tests on the network defined
  setup <binaries...>                    Setup is meant for downloading and making dev environment of Zombienet ready
  version                                Prints zombienet version
  help [command]                         display help for command

Setting up our config

Zombienet works with a config. The config is where you specify your test network's configuration. The config is also where we specify our binaries.

Create a config file:

touch config.toml

So we want to specify in our config that we want a Rococo relay chain with four validators and two parachains each with one collator:

[relaychain]
default_command = "./bin/polkadot-v0.9.32"
default_args = [ "-lparachain=debug" ]

chain = "rococo-local"
  # relaychain nodes are by default validators
  [[relaychain.nodes]]
  name = "alice"

  [[relaychain.nodes]]
  name = "bob"

  [[relaychain.nodes]]
  name = "charlie"

  [[relaychain.nodes]]
  name = "dave"

[[parachains]]
id = 1000
cumulus_based = true

  [parachains.collator]
  name = "parachain-A-1000-collator-01"
  command = "./bin/parachain-template-node-v0.9.32"

[[parachains]]
id = 1001
cumulus_based = true

  [parachains.collator]
  name = "parachain-B-1001-collator-01"
  command = "../bin/parachain-template-node-v0.9.32"

Save the file and run the Zombienet CLI using this config:

./zombienet-macos -p native spawn config.toml

You should see some nicely formatted output stating that the nodes are up and ready.

You can now interact with your nodes on polkadotJS apps!

Opening HRMP Channels

Background on HRMP (XCMP-Lite)

While XCMP is still being implemented, a stop-gap
protocol (see definition below) known as Horizontal Relay-routed
Message Passing (HRMP) exists in its place. HRMP has the same
interface and functionality as XCMP but is much more demanding on
resources since it stores all messages in the Relay Chain storage.
When XCMP has been implemented, HRMP is planned to be deprecated and
phased out in favor of it.

This post will show how to open HRMP channels between two parachains: Parachain A and Parachain B.

Channels are unidirectional

The parachain that wants to open an HRMP channel must make a request to the parachain it wishes to have an open channel with. Once that is done, the other parachain needs to accept this request. This is what we mean by unidirectional flow. For bidirectional communication we need to open another channel in the opposite way.

A channel can be opened only after the recipient confirms it and only on a session change.

Initiate an open channel request from Parachain A to Parachain B

We will have Parachain A initiate a request to open an HRMP channel with Parachain B.

This is done on the relay chain.

Open the relay chain's polkadotJS apps and create the following extrinsic:

​​​hrmp.hrmpInitOpenChannel(
​​​    recipient: 2000                    // the other parachain you want to open the channel with
​​​    proposedMaxCapacity: 1000          // specifies how many messages can be in the channel at once
​​​    proposed_max_message_size: 102400  // specifies the maximum size of the messages
​​​)

We will not submit this transaction. Instead, after setting the desired parameters, copy the encoded call data. We will need this later to craft our XCM message.

Here is a past example of an encoded call data in Rococo: 0x1700b80b0000e803000000900100

Note that proposedMaxCapacity and proposed_max_message_size numbers are subject to the relay chain's configuration limits. The relay chain's configuration can be found in configuration.activeConfig():

The image above is an example of Rococo's active configuration at the time of writing this article.

The next step is done on parachain A.

Connect to Parachain A on polkadotJS apps.

Create a polkadotXcm extrinsic to notify the relay chain that we want to open a channel with parachain B (using the encoded call data from the previous step):

​​​polkadotXcm.send(
​​​    dest: V1
​​​        parents: 1
​​​        interior: Here
​​​    message: V2
​​​        XcmV2Instruction: WithdrawAsset
​​​            id: Concrete
​​​                parents: 0
​​​                interior: Here
​​​            fun: Fungible
​​​                Fungible: 1_000_000_000_000
​​​        XcmV2Instruction: BuyExecution
​​​            id: Concrete
​​​                parents: 0
​​​                interior: Here
​​​            fun: Fungible
​​​                Fungible: 1_000_000_000_000
​​​            weightLimit: Unlimited
​​​        XcmV2Instruction: Transact
​​​            originType: Native
​​​            requireWeightAtMost: 4_000_000_000
​​​                encoded: 0x1700b80b0000e803000000900100 // our hrmpInitOpenChannel encoded call data

​​​)

Note: you should compose your message taking into account the active XCM configuration, this is just an example.

Accept Parachain A's open channel request on Parachain B

So far Parachain A has done its part: it has requested to open an HRMP channel to Parachain B.

Now this request has to be accepted by Parachain B.

On the relay chain, create the following extrinsic:

​​​hrmp.hrmpAcceptOpenChannel(
​​​    sender: 2000
​​​)

There is no need to submit the transaction. Instead copy the encoded call data.

Here is an example of an encoded call data in Rococo: 0x1701d0070000

Now on Parachain B we use polkadotXcm.send extrinsic to craft an XCM message (using the encoded call data from the previous step).

​​​polkadotXcm.send(
​​​    dest: V1
​​​        parents: 1
​​​        interior: Here
​​​    message: V2
​​​        XcmV2Instruction: WithdrawAsset
​​​            id: Concrete
​​​                parents: 0
​​​                interior: Here
​​​            fun: Fungible
​​​                Fungible: 1_000_000_000_000
​​​        XcmV2Instruction: BuyExecution
​​​            id: Concrete
​​​                parents: 0
​​​                interior: Here
​​​            fun: Fungible
​​​                Fungible: 1_000_000_000_000
​​​            weightLimit: Unlimited
​​​        XcmV2Instruction: Transact
​​​            originType: Native
​​​            requireWeightAtMost: 4_000_000_000
​​​                encoded: 0x1701d0070000 // our hrmpAcceptOpenChannel encoded call data

​​​)

Note: you should compose your message taking into account the active XCM configuration, this is just an example.

And that's it — the channel has been accepted and it will remain open, such that communication from Parachain A to Parachain B can now flow.

For making this a bidirectional channel you'll need to open another channel, from Parachain B to Parachain A. You can do this by repeating the steps above (inversely).

More info here:

Zombienet HRMP Configuration

Zombienet also has an HRMP configuration to quickly open HRMP channels.

For testing purposes this can be useful.

Simply add the following to your config:

[[hrmpChannels]]
sender = 1000
recipient = 1001
maxCapacity = 1000
maxMessageSize = 102400

[[hrmpChannels]]
sender = 1001
recipient = 1000
maxCapacity = 1000
maxMessageSize = 102400

Restart Zombienet and you now have a bidirectional HRMP channel open between the two parachains!

Select a repo