Skip to content

Running the network

  • Docker, with about 4 GB of memory free. The stack uses about 2.5 GB when it is idle.
  • Go 1.27 or newer, for the faucet.
  • Python 3, which the scripts use to read the node’s JSON replies.

Let’s get a copy of the stack and start it:

Terminal window
git clone https://github.com/twostack/localnet-teranode.git
cd localnet-teranode
./scripts/start.sh

There is a bit of work behind that one command. The first time we run it, start.sh:

  1. writes a .env file with fresh secrets (the RPC password, the Postgres password, and the node’s libp2p identity),
  2. starts the containers,
  3. switches Teranode from IDLE to RUNNING, since a fresh node mines nothing until it is told to,
  4. creates the faucet’s Private Key,
  5. mines to height 101, so that the faucet has a block reward it can spend,
  6. waits for Arcade to answer.

The first run also has to pull the images, so it takes a few minutes. After that, start.sh is safe to run again at any time. It only does the steps that still need doing.

Do note that the chain is persistent. It survives stop.sh, container restarts and rebooting the machine. Only reset.sh wipes it.

status.sh tells us whether everything is up:

Terminal window
./scripts/status.sh

It lists the containers, the node’s state, the chain tip, whether Arcade is healthy, whether Merkle Service is connected to Teranode’s gossip, and how much the faucet can spend.

The Merkle Service line is the one to keep an eye on. Without that connection Arcade can’t follow a Transaction past ACCEPTED_BY_NETWORK, and no merkle proofs come back.

The autominer mines a block every 10 minutes. When we don’t want to wait, we mine one ourselves:

Terminal window
./scripts/mine.sh # one block
./scripts/mine.sh 10 # ten blocks

The block reward goes to the faucet, unless we give mine.sh an Address as its second argument. We will make use of that in Getting coins.

The interval is MINE_INTERVAL in .env, in seconds. Setting it to 0 turns the autominer off, which is what we want for tests that must not see a block they didn’t ask for. Run ./scripts/start.sh again after changing it.

Terminal window
./scripts/stop.sh # stop the stack, keep the chain
./scripts/reset.sh # stop the stack and wipe the chain

reset.sh keeps the faucet’s key and .env. The next start.sh mines a fresh chain to height 101.

Everything listens on 127.0.0.1. Most applications only need the first two rows.

Port Service What it’s for
23011 Arcade The ARC-compatible API. There is no /v1 prefix. Health check on 23012, SSE stream on 23013
18090 DataHub http://127.0.0.1:18090/api/v1 for blocks, headers, Transactions and UTXOs, with a block explorer at /
19292 RPC Teranode’s JSON-RPC, with basic auth (rpc_user and rpc_pass in .env)
18444 Wire protocol Bitcoin P2P, for header sync
18084 Propagation Teranode’s own POST /tx, which bypasses Arcade
19905 libp2p Teranode’s gossip
28080 Merkle Service Used by Arcade. We rarely need it
25432 / 29092 / 23000 Postgres / Redpanda / Aerospike Teranode’s data stores

Testing from a phone? Set HOST_IP=0.0.0.0 in .env and run ./scripts/start.sh again. Arcade, its SSE stream, the DataHub and the wire protocol then listen on every interface, and the app on the phone uses our computer’s LAN address. Everything else stays on 127.0.0.1.

A service that runs in Docker itself can join the localnet-teranode network instead, and use the container names:

services:
my-test-service:
networks: [localnet-teranode]
environment:
ARC_URL: http://arcade:8080
networks:
localnet-teranode:
external: true

The full list of endpoints, from the host and from inside the network, is in the repository’s CONSUMING.md.