# cardano-node-tests-antithesis
Antithesis fault-injection test suites for [cardano-node](https://github.com/IntersectMBO/cardano-node).
## What is Antithesis
[Antithesis](https://antithesis.com) is a deterministic testing platform that runs software inside a fully simulated environment. It controls every source of non-determinism — network, disk, clocks, thread scheduling — so any bug it finds can be reproduced exactly from a saved seed. During a run it continuously injects faults (network partitions, process kills, pauses) and uses coverage-guided exploration to find states that trigger bugs or violate assertions.
For cardano-node this means we can verify that the node survives and recovers from realistic failure scenarios — isolated producers, split-brain partitions, killed peers — that are hard to reproduce reliably in a conventional test environment.
## Relationship to cardano-node-tests
[cardano-node-tests](https://github.com/IntersectMBO/cardano-node-tests) is the main functional test suite for cardano-node. It runs against a local cluster and covers correctness of individual features.
This repo is complementary: it focuses on **fault tolerance and recovery** rather than functional correctness. The workloads are long-running, continuous, and designed to keep driving governance operations while Antithesis injects faults underneath. The Python drivers reuse [`cardano-clusterlib`](https://github.com/IntersectMBO/cardano-clusterlib) — the standalone cardano-cli wrapper library extracted from cardano-node-tests — so the same library underpins both suites.
## How it works
Each testnet is a Docker Compose stack. Antithesis boots the stack, waits for it to be healthy, then runs in a loop:
1. Inject a fault (kill a producer, partition the network, pause a process).
2. `docker exec` a composer driver into the workload container to perform an operation.
3. Collect coverage signals (Antithesis SDK assertions) to guide the next fault.
The workload container is a sleeping cardano-cli image (`gov-cli`). Each logical operation is a separate script so Antithesis can schedule them independently. Assertions use `always` / `sometimes` / `reachable` primitives from the Antithesis SDK to tell the platform what properties must hold and what states it should try to reach.
## Repository structure
```
components/ Docker image build contexts shared across testnets
gov-cli/ Sleeping cardano-cli container; holds the governance composer drivers
gov-configurator/ One-shot init container; generates Conway genesis + governance assets
testnets/ One directory per Antithesis testnet
cardano_node_governance/ Conway governance workload under fault injection
```
## Testnets
### cardano_node_governance
Exercises Conway on-chain governance continuously under fault injection against a 3-producer + 1-relay network:
- **Setup**: registers DReps, authorizes Constitutional Committee hot keys, delegates vote-stake.
- **Workload**: continuously submits InfoActions and casts DRep / SPO / CC votes on live actions. InfoActions never enact, so the workload is unbounded and chain state never drifts. Also submits TreasuryWithdrawals actions and casts DRep / CC votes on them (SPOs can't vote on this action type); unlike InfoActions these do ratify and enact, exercising the enactment/treasury-debit path, kept safe by a small bounded transfer amount per action.
- **Invariants**: checks that governance state is well-formed, committee quorum is maintained after faults, and votes are recorded after recovery.
- **Perturbation witness**: samples whether block production stalled under active faults, giving Antithesis a signal to guide fault scheduling toward governance operations on a degraded chain.
The Python drivers use `cardano-clusterlib`. Genesis and governance assets are generated by [cardonnay](https://github.com/IntersectMBO/cardonnay)'s `local_fast` generator via the `gov-configurator` init container.
See [`testnets/cardano_node_governance/README.md`](testnets/cardano_node_governance/README.md) for topology and validation steps, and [`testnets/cardano_node_governance/PROPERTIES.md`](testnets/cardano_node_governance/PROPERTIES.md) for every assertion the drivers emit.
## Prerequisites
- Docker and Docker Compose v2
- Access to pull images from `ghcr.io/intersectmbo` and `ghcr.io/cardano-foundation`
- tAda in the moog requester wallet, before dispatching the Antithesis
workflow. An empty wallet does not fail `create-test`: the request is
accepted and then never picked up, so the run sits in `pending`.
`scripts/wait-for-test.sh` gives up after `PENDING_TIMEOUT` (30min by
default) and says so, but checking first avoids the wasted dispatch -
`moog wallet info` prints the address, and any preprod explorer shows
its balance. Top up from the Cardano preprod faucet.
## Upstreams
Two Cardano Foundation repos are load-bearing here but appear only as a workflow flag and as image
paths, so they're easy to miss:
- [cardano-foundation/moog](https://github.com/cardano-foundation/moog) — the CLI that submits and
polls Antithesis test runs. Pinned in `.github/workflows/cardano-node-governance.yaml`; see the
comment there for why not `latest`.
- [cardano-foundation/cardano-node-antithesis](https://github.com/cardano-foundation/cardano-node-antithesis)
— source for the `sidecar`, `tracer-sidecar` and `log-tailer` images this testnet reuses. The
consensus-safety properties in the Antithesis report that aren't in `PROPERTIES.md` (e.g.
`cluster fork depth < k`) come from there, not from this repo.
## Running locally
From the testnet directory:
```bash
cd testnets/cardano_node_governance
docker compose build
docker compose up
```