# 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: - https://github.com/paritytech/zombienet ## 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](https://github.com/paritytech/zombienet#requirements-by-provider). 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: > - https://github.com/paritytech/polkadot/releases 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](https://github.com/substrate-developer-hub/substrate-parachain-template) binary. We need this binary to spin up our parachains! Let's clone down [substrate-parachain-template](https://github.com/substrate-developer-hub/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: - https://github.com/paritytech/zombienet/releases 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](https://polkadot.js.org/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. - https://wiki.polkadot.network/docs/learn-xcm#hrmp-xcmp-lite 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: ```js 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 ) ``` ![](https://i.stack.imgur.com/AnPOl.png) 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`](https://polkadot.js.org/apps/?rpc=wss%3A%2F%2Frococo-rpc.polkadot.io#/extrinsics/decode/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()`: ![](https://i.stack.imgur.com/oNE6o.png) > 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): ```js 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: ```js 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`](https://polkadot.js.org/apps/?rpc=wss%3A%2F%2Frococo-rpc.polkadot.io#/extrinsics/decode/0x1701d0070000) Now on Parachain B we use `polkadotXcm.send` extrinsic to craft an XCM message (using the encoded call data from the previous step). ```js 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 &mdash; 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: - https://docs.substrate.io/reference/how-to-guides/parachains/add-hrmp-channels ## 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!