Skip to content

Latest commit

 

History

History
243 lines (191 loc) · 11.4 KB

File metadata and controls

243 lines (191 loc) · 11.4 KB

DevNet

A self-contained, two-pool private Cardano network for end-to-end testing of Dingo as a block producer alongside the upstream cardano-node. The whole thing runs locally under Docker Compose: a configurator container generates fresh genesis files and pool keys on each start, then three nodes come up and forge blocks against each other while a txpump sidecar continuously submits payment transactions into Dingo's mempool.

The Go test harness lives alongside this directory: internal/test/devnet/ (helpers and config loader at the top level, runnable scenarios under internal/test/devnet/scenarios/). The layout mirrors internal/test/antithesis/. All test files are gated by the devnet build tag.

Topology

Service Role Default host port Container IP
configurator One-shot: generates keys + genesis
dingo-producer Dingo, forging with pool 1 keys 30103001 172.20.0.10
cardano-producer cardano-node, forging with pool 2 30113001 172.20.0.11
cardano-relay cardano-node relay (no forging) 30123001 172.20.0.12
txpump Submits payment txs into Dingo 172.20.0.20

Pool 1 and pool 2 are wired into a ring topology by configurator.sh. The relay peers with both producers so block diffusion is exercised in addition to direct producer-to-producer sync.

The txpump container is a load generator: it talks Ouroboros NtC over Dingo's UNIX socket and submits randomised payment transactions in batches (10–50 txs per round, 5–15s cooldown), funded from the genesis UTxO keys generated by the configurator. It exists to keep the mempool exercised while the consensus tests run, so block bodies are non-empty and tx-submission / mempool paths are continuously hit. The image is built from internal/test/antithesis/ (Dockerfile.txpump, cmd/txpump/).

Network parameters

Driven by testnet.yaml. Notable values:

  • networkMagic: 42
  • epochLength: 500 slots, slotLength: 1s (~8 min epochs)
  • activeSlotsCoeff: 0.4, securityParam (k): 40
  • All hard forks at epoch 0 — the network starts in Conway with protocol version 10.0.
  • systemStart is set to now + 30s by the configurator after key generation, so nodes have time to come up before slot 0.

Prerequisites

  • Docker with the Compose plugin (docker compose version must work).
  • Go 1.25+ on the host (matching go.mod) to run the integration tests.
  • Outbound network access on first run to pull ghcr.io/blinklabs-io/cardano-node:11.0.1 and to clone cardano-foundation/testnet-generation-tool inside the configurator image.
  • Local build of Dingo via the repo root Dockerfile — Compose builds it automatically on up / run-tests.sh.
  • Local build of txpump from ../antithesis/ — also built automatically by Compose.

Building Dingo

You do not need to make build Dingo locally — Compose builds the dingo-producer image directly from the repo by referencing the top-level Dockerfile (build.context: ../../.. in docker-compose.yml). The build runs make build inside ghcr.io/blinklabs-io/go:1.26.1-1 against your working tree, so any uncommitted local changes are included.

Force a rebuild after editing Dingo source:

# from this directory (internal/test/devnet)
docker compose -f docker-compose.yml build dingo-producer

# or from the repo root
docker compose -f internal/test/devnet/docker-compose.yml build dingo-producer

run-tests.sh always rebuilds dingo-producer before starting. start.sh does not — it only runs up -d, so add an explicit build (or pass --build to up) when you want fresh code in the running container.

The configurator image (Dockerfile.configurator) is built from this directory and pinned to upstream testnet-generation-tool@v0.1.0; it rarely needs rebuilding once cached.

Where to run from

start.sh, stop.sh, and run-tests.sh all resolve their own location via SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)", so they work from any current working directory:

# from this directory
./start.sh

# from the repo root
./internal/test/devnet/start.sh

# from anywhere
/home/me/dingo/internal/test/devnet/run-tests.sh -run TestBasic

Plain docker compose invocations are not location-independent — pass -f <path>/docker-compose.yml if you are not in this directory, since the compose file's build.context paths (. and ../../..) are resolved relative to the compose file itself, not your shell's CWD.

The scripts must, however, stay at this path inside the repo: the compose file references ../../.. to pick up the Dingo source tree for its build context, and the topology/*.json and testnet.yaml files are mounted by relative path. Don't copy the directory elsewhere expecting it to work standalone.

Manual usage

./start.sh        # docker compose up -d (configurator runs first, then nodes)
./stop.sh         # docker compose down -v (also removes volumes)

Tail logs while it runs:

docker compose -f docker-compose.yml logs -f
docker compose -f docker-compose.yml logs -f dingo-producer

Open a shell inside a node:

docker exec -it dingo-producer sh
docker exec -it cardano-producer bash

The Dingo node socket is on the dingo-producer-ipc volume at /ipc/dingo.socket; the cardano-node sockets live on each node's *-ipc volume at /ipc/node.socket.

Running the integration tests

run-tests.sh is the entry point used both locally and in CI:

./run-tests.sh                              # bring up the network, run all devnet tests, tear down
./run-tests.sh -run TestBasicBlockForging   # forward -run (and other flags) to `go test`
./run-tests.sh --keep-up                    # leave the network running on success (for poking around)

What it does:

  1. Builds the dingo-producer image.
  2. docker compose up -d and waits up to 120s for all three nodes to report healthy. (The txpump container has no healthcheck — it depends on dingo-producer being healthy and starts pumping from there.)
  3. From the repo root, runs go test -tags devnet -count=1 -v ./internal/test/devnet/....
  4. Tears the network down. On failure it dumps the last 100 lines of compose logs first.

The harness reads endpoint addresses from DEVNET_DINGO_ADDR, DEVNET_CARDANO_ADDR, and DEVNET_RELAY_ADDR, which run-tests.sh sets based on the host port mappings. Tests also re-parse testnet.yaml at runtime via devnet.LoadDevNetConfig(), so changes to network parameters flow through without code edits — override the path with DEVNET_TESTNET_YAML if you stage a non-default spec.

Test scenarios

All scenarios live in internal/test/devnet/scenarios/ and use the harness in internal/test/devnet/:

Test What it verifies
TestBasicBlockForging Dingo forges at least one block and all three nodes converge within securityParam
TestDingoChainAdvances Dingo's tip slot advances over a short observation window
TestCardanoProducerChainAdvances cardano-producer's tip advances (sanity for the reference node)
TestChainGrowthRate Block production rate is within the expected range derived from activeSlotsCoeff and slotLength
TestRelayPropagation Blocks reach the non-forging relay
TestSustainedConsensus All nodes stay in agreement across multiple sampling intervals
TestEpochBoundaryConsensus Dingo and cardano-node remain in consensus across at least one epoch boundary (exercises candidate-nonce freeze, lab nonce roll, and new-epoch VRF verification)

Port and address overrides

If the defaults clash with something already on the host, override the port mappings via environment or a local .env file next to docker-compose.yml:

Variable Default Used by
DEVNET_DINGO_PORT 3010 docker-compose host port for Dingo
DEVNET_CARDANO_PORT 3011 docker-compose host port for cardano
DEVNET_RELAY_PORT 3012 docker-compose host port for relay
DINGO_PORT falls back to DEVNET_DINGO_PORT run-tests.sh only
CARDANO_PORT falls back to DEVNET_CARDANO_PORT run-tests.sh only
RELAY_PORT falls back to DEVNET_RELAY_PORT run-tests.sh only
TEST_TIMEOUT 5m go test -timeout in run-tests.sh

run-tests.sh derives DEVNET_DINGO_ADDR etc. from these so the test harness and the compose port mappings always agree.

Files

File Purpose
docker-compose.yml Service, volume, and network definitions
Dockerfile.configurator Builds the genesis/key generator image (cardano-foundation/testnet-generation-tool v0.1.0)
configurator.sh Runs inside the configurator: drives genesis-cli.py, builds ring topology, sets systemStart, relaxes key permissions for non-root node containers
testnet.yaml Network spec consumed by genesis-cli.py (pool count, slot params, hard fork schedule)
topology/dingo-producer.json, cardano-producer.json, relay.json Static peer lists mounted into each node
start.sh / stop.sh Convenience wrappers around docker compose up -d / down -v
run-tests.sh Full bring-up → test → tear-down cycle used by CI
../antithesis/Dockerfile.txpump, ../antithesis/cmd/txpump/ Source for the txpump load generator image
harness.go, config.go Go test harness package: Ouroboros NtN client, tip queries, consensus checks, testnet.yaml loader (build tag devnet)
harness_test.go Tests for the harness itself (build tag devnet)
scenarios/ Devnet test scenarios (one or more Test* per file, all //go:build devnet)

Cleanup

stop.sh and the run-tests.sh trap both run docker compose down -v, which removes the per-pool config volumes (p1-configs, p2-configs, utxo-keys) and node databases. Genesis is regenerated on every start, so this is the desired default — there's no state worth preserving between runs. If a previous run left orphaned containers or volumes around:

docker compose -f docker-compose.yml down -v --remove-orphans
docker volume ls | grep devnet                       # inspect leftovers

Troubleshooting

  • Nodes don't reach healthy within 120s. run-tests.sh dumps docker compose ps and the last 100 log lines on timeout. Most often this is a build issue with the Dingo image or genesis-time skew (the configurator schedules systemStart 30s in the future; if your machine's clock is far off, regenerate by tearing down and starting again).
  • port is already allocated. Set the DEVNET_*_PORT overrides above.
  • Configurator fails on first run. It clones cardano-foundation/testnet-generation-tool at build time; check outbound network access and re-run docker compose build configurator.
  • Dingo image is stale. run-tests.sh rebuilds dingo-producer on every invocation; for start.sh runs, force a rebuild with docker compose -f docker-compose.yml build dingo-producer.