Skip to main content

Create a Local Test Network

Introduction

This tutorial explains several methods of creating a local test network.

There are currently two options to launch such a local network:

Metal Network Runner

Installation

The Metal Network Runner repository is hosted at https://github.com/MetalBlockchain/metal-network-runner.

That repository's README details the tool.

To download a binary for the latest release, run:

curl -sSfL https://raw.githubusercontent.com/MetalBlockchain/metal-network-runner/main/scripts/install.sh | sh -s

The script installs the binary inside the ~/bin directory. If the directory doesn't exist, it will be created.

Please make sure that ~/bin is in your $PATH:

export PATH=~/bin:$PATH

To add it to your path permanently, add an export command to your shell initialization script. If you run bash, use .bashrc. If you run zsh, use .zshrc.

Furthermore, METALGO_EXEC_PATH should be set properly in all shells you run commands related to Metal Network Runner. We strongly recommend that you put the following in to your shell's configuration file.

# replace execPath with the path to MetalGo on your machine
# e.g., ${HOME}/go/src/github.com/MetalBlockchain/metalgo/build/metalgo
METALGO_EXEC_PATH="${HOME}/go/src/github.com/MetalBlockchain/metalgo/build/metalgo"

Unless otherwise specified, file paths given below are relative to the root of this repository.

When running with the binary metal-network-runner, it runs a server process as an RPC server which then waits for API calls and handles them. Therefore we run one shell with the RPC server, and another one for issuing calls.

Start the Server

metal-network-runner server \
--log-level debug \
--port=":8080" \
--grpc-gateway-port=":8081"

Note that the above command will run until you stop it with CTRL + C. Further commands will have to be run in a separate terminal.

The RPC server listens to two ports:

  • port: the main gRPC port (see gRPC).
  • grpc-gateway-port: the gRPC gateway port (see gRPC-gateway), which allows for HTTP requests.

When using the binary to issue calls, the main port will be hit. In this mode, the binary executes compiled code to issue calls. Alternatively, plain HTTP can be used to issue calls, without the need to use the binary. In this mode, the grpc-gateway-port should be queried.

Each of the examples below will show both modes, clarifying its usage.

Start a New Metal Network with Five Nodes (a Cluster)

curl -X POST -k http://localhost:8081/v1/control/start -d '{"execPath":"'${METALGO_EXEC_PATH}'","numNodes":5,"logLevel":"INFO"}'

or

metal-network-runner control start \
--log-level debug \
--endpoint="0.0.0.0:8080" \
--number-of-nodes=5 \
--metalgo-path ${METALGO_EXEC_PATH}

Response

{
"clusterInfo": {
"nodeNames": [],
"nodeInfos": {},
"pid": 98315,
"rootDataDir": "/var/folders/0h/v4nrbbsn1vvbr5h2wfrh5h500000gn/T/network-runner-root-data3575458647",
"healthy": false,
"attachedPeerInfos": {},
"customVmsHealthy": false,
"customVms": {}
}
}

Use this command to check if all the nodes in the cluster are healthy

curl -X POST -k http://localhost:8081/v1/control/health -d ''

or

metal-network-runner control health \
--log-level debug \
--endpoint="0.0.0.0:8080"

The response to this call is actually pretty large, as it contains the state of the whole cluster. At the very end of it there should be a text saying healthy:true (it would say false if it wasn't healthy).

{
"clusterInfo": {
"nodeNames": ["node3", "node4", "node5", "node1", "node2"],
"nodeInfos": {
"node1": {
"name": "node1",
"execPath": "/Users/testuser/workspace/src/github.com/MetalBlockchain/metalgo/build/metalgo",
"uri": "http://127.0.0.1:40108",
"id": "NodeID-7Xhw2mDxuDS44j42TCB6U5579esbSt3Lg",
"logDir": "/var/folders/0h/v4nrbbsn1vvbr5h2wfrh5h500000gn/T/network-runner-root-data3575458647/node1/log",
"dbDir": "/var/folders/0h/v4nrbbsn1vvbr5h2wfrh5h500000gn/T/network-runner-root-data3575458647/node1/db-dir",
"pluginDir": "",
"whitelistedSubnets": "",
"config": "eyJhcGktYWRtaW4tZW5hYmxlZCI6dHJ1ZSwiYXBpLWlwY3MtZW5hYmxlZCI6dHJ1ZSwiZGItZGlyIjoiL3Zhci9mb2xkZXJzLzBoL3Y0bnJiYnNuMXZ2YnI1aDJ3ZnJoNWg1MDAwMDBnbi9UL25ldHdvcmstcnVubmVyLXJvb3QtZGF0YTM1NzU0NTg2NDcvbm9kZTEvZGItZGlyIiwiaGVhbHRoLWNoZWNrLWZyZXF1ZW5jeSI6IjJzIiwiaW5kZXgtZW5hYmxlZCI6dHJ1ZSwibG9nLWRpciI6Ii92YXIvZm9sZGVycy8waC92NG5yYmJzbjF2dmJyNWgyd2ZyaDVoNTAwMDAwZ24vVC9uZXR3b3JrLXJ1bm5lci1yb290LWRhdGEzNTc1NDU4NjQ3L25vZGUxL2xvZyIsImxvZy1kaXNwbGF5LWxldmVsIjoiSU5GTyIsImxvZy1sZXZlbCI6IklORk8iLCJuZXR3b3JrLW1heC1yZWNvbm5lY3QtZGVsYXkiOiIxcyIsIm5ldHdvcmstcGVlci1saXN0LWdvc3NpcC1mcmVxdWVuY3kiOiIyNTBtcyIsInBsdWdpbi1kaXIiOiIiLCJwdWJsaWMtaXAiOiIxMjcuMC4wLjEiLCJ3aGl0ZWxpc3RlZC1zdWJuZXRzIjoiIn0="
},
"node2": {
"name": "node2",
"execPath": "/Users/testuser/workspace/src/github.com/MetalBlockchain/metalgo/build/metalgo",
"uri": "http://127.0.0.1:64470",
"id": "NodeID-MFrZFVCXPv5iCn6M9K6XduxGTYp891xXZ",
"logDir": "/var/folders/0h/v4nrbbsn1vvbr5h2wfrh5h500000gn/T/network-runner-root-data3575458647/node2/log",
"dbDir": "/var/folders/0h/v4nrbbsn1vvbr5h2wfrh5h500000gn/T/network-runner-root-data3575458647/node2/db-dir",
"pluginDir": "",
"whitelistedSubnets": "",
"config": "eyJhcGktYWRtaW4tZW5hYmxlZCI6dHJ1ZSwiYXBpLWlwY3MtZW5hYmxlZCI6dHJ1ZSwiZGItZGlyIjoiL3Zhci9mb2xkZXJzLzBoL3Y0bnJiYnNuMXZ2YnI1aDJ3ZnJoNWg1MDAwMDBnbi9UL25ldHdvcmstcnVubmVyLXJvb3QtZGF0YTM1NzU0NTg2NDcvbm9kZTIvZGItZGlyIiwiaGVhbHRoLWNoZWNrLWZyZXF1ZW5jeSI6IjJzIiwiaW5kZXgtZW5hYmxlZCI6dHJ1ZSwibG9nLWRpciI6Ii92YXIvZm9sZGVycy8waC92NG5yYmJzbjF2dmJyNWgyd2ZyaDVoNTAwMDAwZ24vVC9uZXR3b3JrLXJ1bm5lci1yb290LWRhdGEzNTc1NDU4NjQ3L25vZGUyL2xvZyIsImxvZy1kaXNwbGF5LWxldmVsIjoiSU5GTyIsImxvZy1sZXZlbCI6IklORk8iLCJuZXR3b3JrLW1heC1yZWNvbm5lY3QtZGVsYXkiOiIxcyIsIm5ldHdvcmstcGVlci1saXN0LWdvc3NpcC1mcmVxdWVuY3kiOiIyNTBtcyIsInBsdWdpbi1kaXIiOiIiLCJwdWJsaWMtaXAiOiIxMjcuMC4wLjEiLCJ3aGl0ZWxpc3RlZC1zdWJuZXRzIjoiIn0="
},
"node3": {
"name": "node3",
"execPath": "/Users/testuser/workspace/src/github.com/MetalBlockchain/metalgo/build/metalgo",
"uri": "http://127.0.0.1:30301",
"id": "NodeID-NFBbbJ4qCmNaCzeW7sxErhvWqvEQMnYcN",
"logDir": "/var/folders/0h/v4nrbbsn1vvbr5h2wfrh5h500000gn/T/network-runner-root-data3575458647/node3/log",
"dbDir": "/var/folders/0h/v4nrbbsn1vvbr5h2wfrh5h500000gn/T/network-runner-root-data3575458647/node3/db-dir",
"pluginDir": "",
"whitelistedSubnets": "",
"config": "eyJhcGktYWRtaW4tZW5hYmxlZCI6dHJ1ZSwiYXBpLWlwY3MtZW5hYmxlZCI6dHJ1ZSwiZGItZGlyIjoiL3Zhci9mb2xkZXJzLzBoL3Y0bnJiYnNuMXZ2YnI1aDJ3ZnJoNWg1MDAwMDBnbi9UL25ldHdvcmstcnVubmVyLXJvb3QtZGF0YTM1NzU0NTg2NDcvbm9kZTMvZGItZGlyIiwiaGVhbHRoLWNoZWNrLWZyZXF1ZW5jeSI6IjJzIiwiaW5kZXgtZW5hYmxlZCI6dHJ1ZSwibG9nLWRpciI6Ii92YXIvZm9sZGVycy8waC92NG5yYmJzbjF2dmJyNWgyd2ZyaDVoNTAwMDAwZ24vVC9uZXR3b3JrLXJ1bm5lci1yb290LWRhdGEzNTc1NDU4NjQ3L25vZGUzL2xvZyIsImxvZy1kaXNwbGF5LWxldmVsIjoiSU5GTyIsImxvZy1sZXZlbCI6IklORk8iLCJuZXR3b3JrLW1heC1yZWNvbm5lY3QtZGVsYXkiOiIxcyIsIm5ldHdvcmstcGVlci1saXN0LWdvc3NpcC1mcmVxdWVuY3kiOiIyNTBtcyIsInBsdWdpbi1kaXIiOiIiLCJwdWJsaWMtaXAiOiIxMjcuMC4wLjEiLCJ3aGl0ZWxpc3RlZC1zdWJuZXRzIjoiIn0="
},
"node4": {
"name": "node4",
"execPath": "/Users/testuser/workspace/src/github.com/MetalBlockchain/metalgo/build/metalgo",
"uri": "http://127.0.0.1:31072",
"id": "NodeID-GWPcbFJZFfZreETSoWjPimr846mXEKCtu",
"logDir": "/var/folders/0h/v4nrbbsn1vvbr5h2wfrh5h500000gn/T/network-runner-root-data3575458647/node4/log",
"dbDir": "/var/folders/0h/v4nrbbsn1vvbr5h2wfrh5h500000gn/T/network-runner-root-data3575458647/node4/db-dir",
"pluginDir": "",
"whitelistedSubnets": "",
"config": "eyJhcGktYWRtaW4tZW5hYmxlZCI6dHJ1ZSwiYXBpLWlwY3MtZW5hYmxlZCI6dHJ1ZSwiZGItZGlyIjoiL3Zhci9mb2xkZXJzLzBoL3Y0bnJiYnNuMXZ2YnI1aDJ3ZnJoNWg1MDAwMDBnbi9UL25ldHdvcmstcnVubmVyLXJvb3QtZGF0YTM1NzU0NTg2NDcvbm9kZTQvZGItZGlyIiwiaGVhbHRoLWNoZWNrLWZyZXF1ZW5jeSI6IjJzIiwiaW5kZXgtZW5hYmxlZCI6dHJ1ZSwibG9nLWRpciI6Ii92YXIvZm9sZGVycy8waC92NG5yYmJzbjF2dmJyNWgyd2ZyaDVoNTAwMDAwZ24vVC9uZXR3b3JrLXJ1bm5lci1yb290LWRhdGEzNTc1NDU4NjQ3L25vZGU0L2xvZyIsImxvZy1kaXNwbGF5LWxldmVsIjoiSU5GTyIsImxvZy1sZXZlbCI6IklORk8iLCJuZXR3b3JrLW1heC1yZWNvbm5lY3QtZGVsYXkiOiIxcyIsIm5ldHdvcmstcGVlci1saXN0LWdvc3NpcC1mcmVxdWVuY3kiOiIyNTBtcyIsInBsdWdpbi1kaXIiOiIiLCJwdWJsaWMtaXAiOiIxMjcuMC4wLjEiLCJ3aGl0ZWxpc3RlZC1zdWJuZXRzIjoiIn0="
},
"node5": {
"name": "node5",
"execPath": "/Users/testuser/workspace/src/github.com/MetalBlockchain/metalgo/build/metalgo",
"uri": "http://127.0.0.1:37730",
"id": "NodeID-P7oB2McjBGgW2NXXWVYjV8JEDFoW9xDE5",
"logDir": "/var/folders/0h/v4nrbbsn1vvbr5h2wfrh5h500000gn/T/network-runner-root-data3575458647/node5/log",
"dbDir": "/var/folders/0h/v4nrbbsn1vvbr5h2wfrh5h500000gn/T/network-runner-root-data3575458647/node5/db-dir",
"pluginDir": "",
"whitelistedSubnets": "",
"config": "eyJhcGktYWRtaW4tZW5hYmxlZCI6dHJ1ZSwiYXBpLWlwY3MtZW5hYmxlZCI6dHJ1ZSwiZGItZGlyIjoiL3Zhci9mb2xkZXJzLzBoL3Y0bnJiYnNuMXZ2YnI1aDJ3ZnJoNWg1MDAwMDBnbi9UL25ldHdvcmstcnVubmVyLXJvb3QtZGF0YTM1NzU0NTg2NDcvbm9kZTUvZGItZGlyIiwiaGVhbHRoLWNoZWNrLWZyZXF1ZW5jeSI6IjJzIiwiaW5kZXgtZW5hYmxlZCI6dHJ1ZSwibG9nLWRpciI6Ii92YXIvZm9sZGVycy8waC92NG5yYmJzbjF2dmJyNWgyd2ZyaDVoNTAwMDAwZ24vVC9uZXR3b3JrLXJ1bm5lci1yb290LWRhdGEzNTc1NDU4NjQ3L25vZGU1L2xvZyIsImxvZy1kaXNwbGF5LWxldmVsIjoiSU5GTyIsImxvZy1sZXZlbCI6IklORk8iLCJuZXR3b3JrLW1heC1yZWNvbm5lY3QtZGVsYXkiOiIxcyIsIm5ldHdvcmstcGVlci1saXN0LWdvc3NpcC1mcmVxdWVuY3kiOiIyNTBtcyIsInBsdWdpbi1kaXIiOiIiLCJwdWJsaWMtaXAiOiIxMjcuMC4wLjEiLCJ3aGl0ZWxpc3RlZC1zdWJuZXRzIjoiIn0="
}
},
"pid": 98315,
"rootDataDir": "/var/folders/0h/v4nrbbsn1vvbr5h2wfrh5h500000gn/T/network-runner-root-data3575458647",
"healthy": true,
"attachedPeerInfos": {},
"customVmsHealthy": false,
"customVms": {}
}
}

To Get API Endpoints of All Nodes in the Cluster

curl -X POST -k http://localhost:8081/v1/control/uris -d ''

or

metal-network-runner control uris \
--log-level debug \
--endpoint="0.0.0.0:8080"

Response

{
"uris": [
"http://127.0.0.1:30301",
"http://127.0.0.1:31072",
"http://127.0.0.1:37730",
"http://127.0.0.1:40108",
"http://127.0.0.1:64470"
]
}

Now you have a 5-nodes network with HTTP ports (where API calls should be sent) 30301, 31072, 37730, 40108 , and 64470.

Please refer to the dedicated Metal Network Runner documentation for more information.

Manually

The below commands assume you have MetalGo installed at $GOPATH/src/github.com/MetalBlockchain/metalgo. Each of the five nodes created is a validator. The staking keys for these nodes are in $GOPATH/src/github.com/MetalBlockchain/metalgo/staking/local/staker1.crt, etc.

The 5 nodes will have HTTP ports (where API calls should be sent) 9650, 9652, 9654, 9656 , and 9658.

To start the network:

cd $GOPATH/src/github.com/MetalBlockchain/metalgo
./scripts/build.sh
./build/metalgo --public-ip=127.0.0.1 --http-port=9650 --staking-port=9651 --db-dir=db/node1 --network-id=local --staking-tls-cert-file=$(pwd)/staking/local/staker1.crt --staking-tls-key-file=$(pwd)/staking/local/staker1.key
./build/metalgo --public-ip=127.0.0.1 --http-port=9652 --staking-port=9653 --db-dir=db/node2 --network-id=local --bootstrap-ips=127.0.0.1:9651 --bootstrap-ids=NodeID-7Xhw2mDxuDS44j42TCB6U5579esbSt3Lg --staking-tls-cert-file=$(pwd)/staking/local/staker2.crt --staking-tls-key-file=$(pwd)/staking/local/staker2.key
./build/metalgo --public-ip=127.0.0.1 --http-port=9654 --staking-port=9655 --db-dir=db/node3 --network-id=local --bootstrap-ips=127.0.0.1:9651 --bootstrap-ids=NodeID-7Xhw2mDxuDS44j42TCB6U5579esbSt3Lg --staking-tls-cert-file=$(pwd)/staking/local/staker3.crt --staking-tls-key-file=$(pwd)/staking/local/staker3.key
./build/metalgo --public-ip=127.0.0.1 --http-port=9656 --staking-port=9657 --db-dir=db/node4 --network-id=local --bootstrap-ips=127.0.0.1:9651 --bootstrap-ids=NodeID-7Xhw2mDxuDS44j42TCB6U5579esbSt3Lg --staking-tls-cert-file=$(pwd)/staking/local/staker4.crt --staking-tls-key-file=$(pwd)/staking/local/staker4.key
./build/metalgo --public-ip=127.0.0.1 --http-port=9658 --staking-port=9659 --db-dir=db/node5 --network-id=local --bootstrap-ips=127.0.0.1:9651 --bootstrap-ids=NodeID-7Xhw2mDxuDS44j42TCB6U5579esbSt3Lg --staking-tls-cert-file=$(pwd)/staking/local/staker5.crt --staking-tls-key-file=$(pwd)/staking/local/staker5.key

Next Step

Check out Fund a Local Test Network.