Skip to content
xDEVS Chain
On this page

Local multi-validator network

You can run a real BFT network of several validators on your own machine, either as plain processes or in Docker with chaos tooling. This is how Phase 2 consensus is developed and tested.

Built (Phase 2) · Static validator set from the genesis file · Dev and test use only

Four validators as processes#

Shell
cargo build

# Keys, one shared genesis.json and node0..node3/config.toml
./target/debug/xdevs-cli testnet init --validators 4 --out ./testnet

# One terminal per validator
./target/debug/xdevs run --config ./testnet/node0/config.toml
./target/debug/xdevs run --config ./testnet/node1/config.toml
./target/debug/xdevs run --config ./testnet/node2/config.toml
./target/debug/xdevs run --config ./testnet/node3/config.toml

cast block-number --rpc-url http://127.0.0.1:30332

All four validators must be up before the first block: one validator holds only 1/4 of the voting power.

Ports#

Node k uses ports base + 10k (default base 30300):

OffsetUsenode0node1node2node3
+0Consensus P2P30300303103032030330
+1Transaction gossip30301303113032130331
+2JSON-RPC (HTTP + WS)30302303123032230332
+3Metrics ([metrics] listen)30303303133032330333

testnet init options#

OptionDefaultMeaning
--validators NrequiredNumber of validators (equal voting power)
--out DIRrequiredOutput directory (must be empty unless --force)
--chain-id ID7117Chain id
--block-time D2sTarget block time
--base-port P30300First port; node k uses P + 10k … P + 10k + 3
--host H127.0.0.1Host the nodes listen on and reach each other at
--full-nodes M0Extra full nodes (no consensus key)
--password / --password-file FILEnoneEncrypt the consensus keys; configs point [validator] password_file at the file
--light-kdfoffCheap scrypt parameters for encrypted keys
--forceoffReplace an existing testnet in --out

Each node directory contains consensus_key.hex (or consensus_key.json when encrypted), node_key.json (libp2p identity, same peer id on both networks), config.toml and, once running, data/. Every node lists every other node as a consensus peer and a gossip bootnode.

Single keys can be generated with:

Shell
xdevs-cli keygen --consensus --out consensus_key.json   # Ed25519 validator key (encrypted keystore)
xdevs-cli keygen --node-key --out node_key.json         # libp2p node identity

What the network guarantees#

With four equal validators:

SituationVoting power onlineExpected
All 4 up4/4Progress
1 stopped, killed, paused or isolated3/4 > 2/3Progress; the node catches up when it returns
2 stopped, or a 2–2 partition2/4 < 2/3Halt, then resume after recovery
Delay or loss on some links4/4Progress, possibly with more rounds
Any situationNever a fork: same block hash and state root at every height on every node

A new full node started late syncs from genesis and follows the head. Each validator keeps a persistent double-sign guard, so a restarted validator never signs two conflicting votes.

Docker localnet#

The Docker setup runs 4 validators, an optional late-joining full node and the load generator, with scripts to inject faults. It needs Linux or macOS with Docker Engine 25+ (on Windows, WSL 2 with Docker Desktop), bash 4+ and jq.

Shell
scripts/localnet-up.sh --build             # build the image, start node0-3, wait for healthy
scripts/check.sh --wait-height 10          # all nodes at >= 10, same hashes and state roots
cast block-number --rpc-url http://127.0.0.1:8545   # node0
scripts/chaos.sh start node4               # late joiner, syncs from genesis
scripts/chaos.sh kill node1                # crash a validator
scripts/chaos.sh partition node0,node1 node2,node3   # the chain halts
scripts/chaos.sh heal                      # and resumes
scripts/localnet-down.sh                   # stop and wipe

The first image build takes 15–30 minutes and about 5 GB of disk. To skip it, use the image CI publishes: export XDEVS_IMAGE=ghcr.io/jacell100/xdevs-chain:main. In Docker, node RPCs are published on 127.0.0.1:8545, :8555, :8565, :8575 (and :8585 for the joiner).

CI runs the same scenarios on every pull request: stop one validator (chain continues), stop two (chain halts), restart (chain resumes), kill -9, pause, isolate, partition, network delay and loss, and a late joiner, followed by a deep fork scan. A separate soak workflow runs the network under load for up to 5.5 hours.

Full instructions: localnet runbook · soak runbook.

Limits today#

  • The validator set is fixed in genesis.json. Staking-driven validator changes, rewards and slashing are Phase 3.
  • Peers are static (persistent_peers, bootnodes); Kademlia discovery is off.
  • P2P runs over TCP; QUIC is planned after a Linux soak.