Zombienet
What is Zombienet?
Zombienet is an integration testing tool that allows users to spawn and test ephemeral substrate based networks.
Why Zombienet?
Integration tests are always complex:
- Setup Configuration
- Port management
- Ready state off all artifacts
- Observability
- Leaking resources
Friction to resolve
- Config flexibility
- Local environment
- Maintenance
- CI friendly
- Scaling
- Test-runner
Goals
Hassle free setup
- Toml / json
- Nice defaults
- Templating lang.
Multiple envs
- Local
- k8s
- podman
Extensible
Custom assertions
Intuitive D.S.L
Templating lang.
Phases
Phases
Spawn
- Custom chain-specs
- Custom command
- Port-mapping
- Parachains registration
Test
- Custom D.S.L
- Multiple assertions
- Extensible
- Custom reporting
Zombienet Options
Download Zombienet
# macOS
curl -L https://github.com/paritytech/zombienet/releases/download/v1.3.63/zombienet-macos
-o ./zombienet
# linux
curl -L https://github.com/paritytech/zombienet/releases/download/v1.3.63/zombienet-linux
-o ./zombienet
# make executable
chmod +x zombienet
Let’s spawn a new network!
But first, try manually…
- Create chain-spec (parachain)
parachain-template-node build-spec --chain local \
--disable-default-bootnode > /tmp/para.json
- Create chain-spec (relay chain)
polkadot build-spec --chain rococo-local \
--disable-default-bootnode > /tmp/relay.json
Add keys*
When not using --alice or --bob, you need to provide additional aura
and grandpa
keys and inject them into the keystore! (per node)
./target/release/polkadot \
key insert --base-path /tmp/node01 \
--chain /tmp/relay.json \
--scheme Sr25519 \
--suri <your-secret-seed> \
--password-interactive \
--key-type aura
./target/release/polkadot key insert \
--base-path /tmp/node01 \
--chain /tmp/relay.json \
--scheme Ed25519 \
--suri <your-secret-key> \
--password-interactive \
--key-type gran
- Start relay chain nodes
# create nodes dirs mkdir -p /tmp/relay/{alice,bob} ./target/release/polkadot \ --alice \ --validator \ --base-path /tmp/relay/alice \ --chain /tmp/relay.json \ --port 30333 \ --ws-port 9944 ./target/release/polkadot \ --bob \ --validator \ --base-path /tmp/relay/bob \ --chain /tmp/relay.json \ --port 30334 \ --ws-port 9945
# create nodes dirs mkdir -p /tmp/relay/{alice,bob} ./target/release/polkadot \ --alice \ --validator \ --base-path /tmp/relay/alice \ --chain /tmp/relay.json \ --port 30333 \ --ws-port 9944 ./target/release/polkadot \ --bob \ --validator \ --base-path /tmp/relay/bob \ --chain /tmp/relay.json \ --port 30334 \ --ws-port 9945
# create nodes dirs mkdir -p /tmp/relay/{alice,bob} ./target/release/polkadot \ --alice \ --validator \ --base-path /tmp/relay/alice \ --chain /tmp/relay.json \ --port 30333 \ --ws-port 9944 ./target/release/polkadot \ --bob \ --validator \ --base-path /tmp/relay/bob \ --chain /tmp/relay.json \ --port 30334 \ --ws-port 9945
- Start collator
# create nodes dirs
mkdir -p /tmp/para/alice
parachain-template-node \
--alice \
--collator \
--force-authoring \
--chain /tmp/para.json \
--base-path /tmp/para/alice \
--port 40333 \
--ws-port 8844 \
-- \
--execution wasm \
--chain /tmp/relay.json \
--port 30343 \
--ws-port 9977
- Register ParaId on relay chain
- Modify parachain chain-spec and create raw format
- Generate genesis wasm and state
- Register parachain using sudo call
parachain-template-node build-spec --chain /tmp/para-raw.json \ --disable-default-bootnode --raw > /tmp/para-raw.json parachain-template-node export-genesis-wasm --chain /tmp/para-raw.json \ para-2000-wasm parachain-template-node export-genesis-state --chain /tmp/para-raw.json \ para-2000-genesis-state
parachain-template-node build-spec --chain /tmp/para-raw.json \ --disable-default-bootnode --raw > /tmp/para-raw.json parachain-template-node export-genesis-wasm --chain /tmp/para-raw.json \ para-2000-wasm parachain-template-node export-genesis-state --chain /tmp/para-raw.json \ para-2000-genesis-state
parachain-template-node build-spec --chain /tmp/para-raw.json \ --disable-default-bootnode --raw > /tmp/para-raw.json parachain-template-node export-genesis-wasm --chain /tmp/para-raw.json \ para-2000-wasm parachain-template-node export-genesis-state --chain /tmp/para-raw.json \ para-2000-genesis-state
Non-trivial chore
- Error prone.
- Multiple commands.
- Port management.
- Multiple process.
Zombienet network definition
Zombienet allow to define your network with a simple configuration file.
# examples/0001-small-network.toml [relaychain] default_image = "docker.io/parity/polkadot:latest" default_command = "polkadot" chain = "rococo-local" [[relaychain.nodes]] name = "sub" [[relaychain.nodes]] name = "zero" [[parachains]] id = 1001 cumulus_based = true [parachains.collator] name = "collator01" image = "docker.io/parity/polkadot-parachain:latest" command = "polkadot-parachain"
# examples/0001-small-network.toml [relaychain] default_image = "docker.io/parity/polkadot:latest" default_command = "polkadot" chain = "rococo-local" [[relaychain.nodes]] name = "sub" [[relaychain.nodes]] name = "zero" [[parachains]] id = 1001 cumulus_based = true [parachains.collator] name = "collator01" image = "docker.io/parity/polkadot-parachain:latest" command = "polkadot-parachain"
# examples/0001-small-network.toml [relaychain] default_image = "docker.io/parity/polkadot:latest" default_command = "polkadot" chain = "rococo-local" [[relaychain.nodes]] name = "sub" [[relaychain.nodes]] name = "zero" [[parachains]] id = 1001 cumulus_based = true [parachains.collator] name = "collator01" image = "docker.io/parity/polkadot-parachain:latest" command = "polkadot-parachain"
Spawn the network
./zombienet spawn examples/0001-small-network.toml
Activity
Try to launch a network with 2
parachains.
Make the network config dynamic
The network definition supports using nunjucks templating language (similar to tera). Where {{variables}} are replaced with env vars and you can use all the built-in features.
[relaychain] default_image = "{{ZOMBIENET_INTEGRATION_IMG}}" default_command = "polkadot"
Make the network config dynamic

Providers
Zombienet providers allow to spawn and test networks with in different environments.
Kubernetes
- Used internally, integrated with the Grafana stack.
- You need to provide your infra stack.
Podman
- Automatically spawn and wire an instance of Grafana stack.
- Attach a jaeger instance if enabled in the network definition.
Native
- Allow to attach to a running Grafana stack. (wip)
Questions?
Meet the Test-runner
Zombienet’s built-in test-runner allows users to use a simple D.S.L. to easily and intuitively write tests with a set of natural language expressions to make assertions.
Built-in assertions
Prometheus: Query the exposed metrics/histograms and assert on their values.
Chain: Query/subscribe chain's storage/events.
Custom scripts: Run custom js scripts or bash scripts (inside the pod).
Node's logs: Match regex/glob patterns in the node's logs.
Integrations: Zombienet supports multiple integrations, like jaeger spans, polkadot introspector and the backchannel.
Description: Small Network Paras Network: ./0002-small-network-paras.toml Creds: config # Only used with k8s # well known functions validator: is up # check all the validators in the group validator-0: parachain 1000 is registered within 225 seconds validator-0: parachain 1001 is registered within 225 seconds # ensure parachains are producing blocks validator-0: parachain 1000 block height is at least 5 within 300 seconds validator-0: parachain 1001 block height is at least 5 within 300 seconds # metrics validator-0: reports node_roles is 4 validator-0: reports block height is at least 2 within 15 seconds # logs (patterns are transformed to regex) validator-1: log line matches glob "*rted #1*" within 10 seconds validator-1: log line matches "Imported #[0-9]+" within 10 seconds # system events (patterns are transformed to regex) validator-2: system event contains "A candidate was included" within 10 seconds validator-2: system event matches glob "*was backed*" within 10 seconds # custom scripts validator-0: js-script ./custom.js with "alice" within 200 seconds validator-0: run ./custom.sh within 200 seconds
Description: Small Network Paras Network: ./0002-small-network-paras.toml Creds: config # Only used with k8s # well known functions validator: is up # check all the validators in the group validator-0: parachain 1000 is registered within 225 seconds validator-0: parachain 1001 is registered within 225 seconds # ensure parachains are producing blocks validator-0: parachain 1000 block height is at least 5 within 300 seconds validator-0: parachain 1001 block height is at least 5 within 300 seconds # metrics validator-0: reports node_roles is 4 validator-0: reports block height is at least 2 within 15 seconds # logs (patterns are transformed to regex) validator-1: log line matches glob "*rted #1*" within 10 seconds validator-1: log line matches "Imported #[0-9]+" within 10 seconds # system events (patterns are transformed to regex) validator-2: system event contains "A candidate was included" within 10 seconds validator-2: system event matches glob "*was backed*" within 10 seconds # custom scripts validator-0: js-script ./custom.js with "alice" within 200 seconds validator-0: run ./custom.sh within 200 seconds
Description: Small Network Paras Network: ./0002-small-network-paras.toml Creds: config # Only used with k8s # well known functions validator: is up # check all the validators in the group validator-0: parachain 1000 is registered within 225 seconds validator-0: parachain 1001 is registered within 225 seconds # ensure parachains are producing blocks validator-0: parachain 1000 block height is at least 5 within 300 seconds validator-0: parachain 1001 block height is at least 5 within 300 seconds # metrics validator-0: reports node_roles is 4 validator-0: reports block height is at least 2 within 15 seconds # logs (patterns are transformed to regex) validator-1: log line matches glob "*rted #1*" within 10 seconds validator-1: log line matches "Imported #[0-9]+" within 10 seconds # system events (patterns are transformed to regex) validator-2: system event contains "A candidate was included" within 10 seconds validator-2: system event matches glob "*was backed*" within 10 seconds # custom scripts validator-0: js-script ./custom.js with "alice" within 200 seconds validator-0: run ./custom.sh within 200 seconds
Description: Small Network Paras Network: ./0002-small-network-paras.toml Creds: config # Only used with k8s # well known functions validator: is up # check all the validators in the group validator-0: parachain 1000 is registered within 225 seconds validator-0: parachain 1001 is registered within 225 seconds # ensure parachains are producing blocks validator-0: parachain 1000 block height is at least 5 within 300 seconds validator-0: parachain 1001 block height is at least 5 within 300 seconds # metrics validator-0: reports node_roles is 4 validator-0: reports block height is at least 2 within 15 seconds # logs (patterns are transformed to regex) validator-1: log line matches glob "*rted #1*" within 10 seconds validator-1: log line matches "Imported #[0-9]+" within 10 seconds # system events (patterns are transformed to regex) validator-2: system event contains "A candidate was included" within 10 seconds validator-2: system event matches glob "*was backed*" within 10 seconds # custom scripts validator-0: js-script ./custom.js with "alice" within 200 seconds validator-0: run ./custom.sh within 200 seconds
Description: Small Network Paras Network: ./0002-small-network-paras.toml Creds: config # Only used with k8s # well known functions validator: is up # check all the validators in the group validator-0: parachain 1000 is registered within 225 seconds validator-0: parachain 1001 is registered within 225 seconds # ensure parachains are producing blocks validator-0: parachain 1000 block height is at least 5 within 300 seconds validator-0: parachain 1001 block height is at least 5 within 300 seconds # metrics validator-0: reports node_roles is 4 validator-0: reports block height is at least 2 within 15 seconds # logs (patterns are transformed to regex) validator-1: log line matches glob "*rted #1*" within 10 seconds validator-1: log line matches "Imported #[0-9]+" within 10 seconds # system events (patterns are transformed to regex) validator-2: system event contains "A candidate was included" within 10 seconds validator-2: system event matches glob "*was backed*" within 10 seconds # custom scripts validator-0: js-script ./custom.js with "alice" within 200 seconds validator-0: run ./custom.sh within 200 seconds
Description: Small Network Paras Network: ./0002-small-network-paras.toml Creds: config # Only used with k8s # well known functions validator: is up # check all the validators in the group validator-0: parachain 1000 is registered within 225 seconds validator-0: parachain 1001 is registered within 225 seconds # ensure parachains are producing blocks validator-0: parachain 1000 block height is at least 5 within 300 seconds validator-0: parachain 1001 block height is at least 5 within 300 seconds # metrics validator-0: reports node_roles is 4 validator-0: reports block height is at least 2 within 15 seconds # logs (patterns are transformed to regex) validator-1: log line matches glob "*rted #1*" within 10 seconds validator-1: log line matches "Imported #[0-9]+" within 10 seconds # system events (patterns are transformed to regex) validator-2: system event contains "A candidate was included" within 10 seconds validator-2: system event matches glob "*was backed*" within 10 seconds # custom scripts validator-0: js-script ./custom.js with "alice" within 200 seconds validator-0: run ./custom.sh within 200 seconds
Description: Small Network Paras Network: ./0002-small-network-paras.toml Creds: config # Only used with k8s # well known functions validator: is up # check all the validators in the group validator-0: parachain 1000 is registered within 225 seconds validator-0: parachain 1001 is registered within 225 seconds # ensure parachains are producing blocks validator-0: parachain 1000 block height is at least 5 within 300 seconds validator-0: parachain 1001 block height is at least 5 within 300 seconds # metrics validator-0: reports node_roles is 4 validator-0: reports block height is at least 2 within 15 seconds # logs (patterns are transformed to regex) validator-1: log line matches glob "*rted #1*" within 10 seconds validator-1: log line matches "Imported #[0-9]+" within 10 seconds # system events (patterns are transformed to regex) validator-2: system event contains "A candidate was included" within 10 seconds validator-2: system event matches glob "*was backed*" within 10 seconds # custom scripts validator-0: js-script ./custom.js with "alice" within 200 seconds validator-0: run ./custom.sh within 200 seconds
DSL extension
Learning a new DSL can be tedious, but if you are using vscode we develop an extension that can help you to write test easily.
Demo time
./zombienet -p native test examples/0002-small-network-paras.zndsl
Extensibility
Zombienet allow users to use the custom-js assertion to extend and run custom tests.
Custom-js
# custom scripts
validator-0: js-script ./custom.js with "alice" within 200 seconds
async function run(nodeName, networkInfo, args) {
const { wsUri, userDefinedTypes } = networkInfo.nodesByName[nodeName];
const api = await zombie.connect(wsUri, userDefinedTypes);
const validator = await api.query.session.validators();
return validator.length;
}
module.exports = { run };
More extensibility
Zombienet also allow users to use as a library to create their own interactions with the running network.
As a Library
@zombienet/orchestrator module expose the start function as entrypoint.
Returning a network instance, with all the information about the running topology.
You can also use the test function passing a callback to run your test.
@zombienet/utils module expose misc utils functions like readNetworkConfig.
import {start} from "@zombienet/orchestrator"; import { readNetworkConfig } from "@zombienet/utils"; const ZOMBIENET_CREDENTIALS = ""; // can be toml or json const launchConfig = readNetworkConfig("../examples/0001-small-network.toml"); ( async () => { const network = await start(ZOMBIENET_CREDENTIALS, launchConfig, { spawnConcurrency: 5, }); // write your own test, `network` will have all the network info })();
import {start} from "@zombienet/orchestrator"; import { readNetworkConfig } from "@zombienet/utils"; const ZOMBIENET_CREDENTIALS = ""; // can be toml or json const launchConfig = readNetworkConfig("../examples/0001-small-network.toml"); ( async () => { const network = await start(ZOMBIENET_CREDENTIALS, launchConfig, { spawnConcurrency: 5, }); // write your own test, `network` will have all the network info })();
import {start} from "@zombienet/orchestrator"; import { readNetworkConfig } from "@zombienet/utils"; const ZOMBIENET_CREDENTIALS = ""; // can be toml or json const launchConfig = readNetworkConfig("../examples/0001-small-network.toml"); ( async () => { const network = await start(ZOMBIENET_CREDENTIALS, launchConfig, { spawnConcurrency: 5, }); // write your own test, `network` will have all the network info })();
import {start} from "@zombienet/orchestrator"; import { readNetworkConfig } from "@zombienet/utils"; const ZOMBIENET_CREDENTIALS = ""; // can be toml or json const launchConfig = readNetworkConfig("../examples/0001-small-network.toml"); ( async () => { const network = await start(ZOMBIENET_CREDENTIALS, launchConfig, { spawnConcurrency: 5, }); // write your own test, `network` will have all the network info })();
import {start} from "@zombienet/orchestrator"; import { readNetworkConfig } from "@zombienet/utils"; const ZOMBIENET_CREDENTIALS = ""; // can be toml or json const launchConfig = readNetworkConfig("../examples/0001-small-network.toml"); ( async () => { const network = await start(ZOMBIENET_CREDENTIALS, launchConfig, { spawnConcurrency: 5, }); // write your own test, `network` will have all the network info })();
import {start} from "@zombienet/orchestrator"; import { readNetworkConfig } from "@zombienet/utils"; const ZOMBIENET_CREDENTIALS = ""; // can be toml or json const launchConfig = readNetworkConfig("../examples/0001-small-network.toml"); ( async () => { const network = await start(ZOMBIENET_CREDENTIALS, launchConfig, { spawnConcurrency: 5, }); // write your own test, `network` will have all the network info })();
The road ahead...
🚧 🚧 Zombienet v2 (a.k.a SDK) is currently under construction 🚧 🚧
The SDK will provide a set of building blocks that users can combine to spawn and interact with the network and also a fluent API for crafting different topologies and assertions for the running network.
Acknowledgement & Contributions
Zombienet take inspiration and some patterns from polkadot-launch and SimNet.
We encourage everyone to test it, provide feedback, ask question and contribute.
Questions?
Activity
Launch a network with two validators and one parachain.
Add a test to ensure:
- block producing
- peers number
- node's role
Additional Resources!
Check speaker notes (click "s" 😉)