Asteria Game
Single workload container that plays the asteria game inside the cardano-node-antithesis cluster. It drives realistic transaction traffic - script spends, minting policies, reference scripts, validity bounds, inline datums - so Antithesis can explore node behavior under real ledger pressure instead of only empty-block production.
This component is part of the phase-1 gatherer feature (#56).
Adding asteria-game to a testnet
Drop the asteria-game container into any testnet's
docker-compose.yaml to give Antithesis a real workload generator on
top of an idle Cardano cluster. The container bundles a long-lived
utxo-indexer plus three short-lived binaries fired by composer
scripts (bootstrap, player, invariant) — see
the why-it's-here section in the master testnet doc
for the rationale.
Pre-requisites
The host testnet must already have:
- A relay that block producers connect to, e.g.
relay1, with a named volume mounted at/stateso the asteria-game container can read its node socket. The master testnet usesrelay1-state:/state. - A
utxo-keysnamed volume populated by the configurator with the genesis wallet skeys (the bootstrap binary needs them to sign the deploy tx). - A network magic of
42. Other magics are not currently wired —NETWORK_MAGICis hard-coded in the player's compose env.
Compose block
Add this service block under services: at the bottom of your
docker-compose.yaml:
asteria-game:
image: ghcr.io/cardano-foundation/cardano-node-antithesis/asteria-game:<tag>
container_name: asteria-game
hostname: asteria-game.example
environment:
INDEXER_SOCK: /tmp/idx.sock
CARDANO_NODE_SOCKET_PATH: /state/node.socket
NETWORK_MAGIC: "42"
volumes:
- relay1-state:/state:ro # read-only N2C socket
- utxo-keys:/utxo-keys:ro # genesis skeys for bootstrap
- asteria-game-db:/idx-db # RocksDB persistence
- asteria-deploy:/asteria-deploy # per-deploy seed TxIn
tmpfs:
- /tmp # holds /tmp/idx.sock
depends_on:
relay1:
condition: service_started
Then declare the two new named volumes under the top-level
volumes: block:
volumes:
# … existing volumes …
asteria-game-db:
asteria-deploy:
<tag> is a 7-char short SHA from a commit on this repo. The
publish-images workflow rebuilds the image and pushes
asteria-game:<short-sha> whenever a commit touches
components/asteria-game/. Pick the tag from any green
publish-images run on main, or use the digest pinned by master
in testnets/cardano-node-releases/docker-compose.yaml.
The composer scripts that drive the binaries are baked into the
image at build time at /opt/antithesis/test/v1/asteria-game/ - nothing extra
to mount. The Antithesis composer mounts that path into its execution
sandbox automatically when the test runs.
Validation gate before merging into a scheduled testnet
Per the project's "no broken container on main testnet" rule, a
new testnet that adds asteria-game must dispatch a 1h Antithesis
run via workflow_dispatch on the feature branch and confirm
findings_new ≤ baseline before merging. The same gate applied
when promoting asteria-game into cardano_node_master.
What does the cluster need to look like
The asteria-game player drives spawnShip Plutus transactions
that consume + replace the asteria UTxO. The cluster needs:
- At least one block producer that accepts valid Plutus v3 transactions (the asteria validators are Aiken-compiled to PlutusV3).
- A relay reachable via
relay1.example:3001(N2N) andrelay1-state:/state/node.socket(N2C). Aliasing it under a different name requires editingcomponents/asteria-game/composer/asteria-game/parallel_driver_asteria_player.sh. - Genesis funds in
utxo-keys/genesis.1.skeysufficient for the one-time bootstrap deploy plus a few thousandspawnShiptransactions over a 3h run.
The master testnet meets all three by construction. New testnets that change the topology, network magic, or genesis layout will need to mirror those choices.
What it does
The container entrypoint is a long-lived utxo-indexer connected to
relay1 via N2C. The image also ships three short-lived binaries that
the Antithesis composer fires as commands:
asteria-bootstrap- serial, idempotent deploy. Picks or reuses a deploy seed UTxO, parameter-applies the validators to that seed, mints the one-shotasteriaAdminNFT, and locks the initialAsteriaDatumat the asteria spend address. Re-running is safe: once the asteria UTxO exists, bootstrap observes it and skips the mint path.asteria-game- parallel workload pass. Reads the asteria UTxO through the indexer, plans a move for one of three logical players, and today lets player 1 build, sign, and submit aspawnShipPlutus transaction. The binary exits after one pass; composer provides the repetition and concurrency.asteria-invariant- one-shot oracle. Depending onASTERIA_INVARIANT, checks either theasteriaAdminsingleton or end-of-run state consistency between the asteriaship_counterand theSHIP*tokens at the spacetime address.
Composer scripts under /opt/antithesis/test/v1/asteria-game/:
| Script | Role |
|---|---|
serial_driver_asteria_bootstrap.sh |
Exclusive bootstrap/deploy command. |
parallel_driver_asteria_player.sh |
Concurrent workload command; one observe/spawn pass. |
parallel_driver_heartbeat.sh |
Indexer readiness probe during the run. |
anytime_asteria_admin_singleton.sh |
Periodic singleton invariant. |
eventually_alive.sh |
Post-fault indexer liveness probe. |
eventually_asteria_consistency.sh |
Post-workload game-state consistency snapshot. |
helper_sdk.sh |
Shared shell SDK emitter and signal-safe wrapper. |
Assertions
Asteria contributes both workload and oracles:
asteria_bootstrap_asteria_created/asteria_bootstrap_already_deployedprove deployment either happened or was observed.asteria_player_ship_spawned_1proves real Plutus spawn transactions landed.asteria_admin_singletonis anAlwaysproperty: exactly oneasteriaAdminNFT exists at the asteria spend address.asteria_state_consistentis aSometimesproperty comparingship_countertoSHIP*token count.asteria_game eventually_alive holdsproves the long-lived indexer is responsive and close to the chain tip.
Build the image
Nix
cd components/asteria-game
nix build .#docker-image
docker load < result
version=$(nix eval --raw .#version)
docker tag \
ghcr.io/cardano-foundation/cardano-node-antithesis/asteria-game:$version \
ghcr.io/cardano-foundation/cardano-node-antithesis/asteria-game:dev
just load-docker-image wraps the same steps.
Run locally
INTERNAL_NETWORK=false docker compose \
-f testnets/cardano-node-releases/docker-compose.yaml up -d
The compose file declares one asteria-game service. Its main process
is the indexer; the Antithesis composer invokes the baked-in scripts
inside that container during a test run.
For a local container smoke:
just logs asteria-game
docker compose -f testnets/cardano-node-releases/docker-compose.yaml \
exec asteria-game /bin/asteria-bootstrap
docker compose -f testnets/cardano-node-releases/docker-compose.yaml \
exec asteria-game /bin/asteria-game
See Also
- License: see LICENSE
- Contributing: see CONTRIBUTING.md
- Other projects by HAL
- Other projects by the Cardano Foundation
- About Cardano