Adversary roadmap
Long-term plan for adversarial workload against the testnet. The adversary component is now a long-running daemon (see Tier 1.1 below); this page tracks where it goes next.
Status
| Tier | Archetype | Status |
|---|---|---|
| 1.1 | chain_sync_flap |
landed (clients PR #103 scaffold, #106 real impl, antithesis PR #99 consumer) |
| 1.2 | chain_sync_thrash |
next — see specs/036-cardano-adversary-thrash/ in cardano-node-clients |
| 1.3 | chain_sync_slow_loris |
not started |
| 2 | block_fetch_replay, tx_submission_*, keepalive_abuse |
not started |
| 3 | upstream-peer Byzantine adversary | epic #91, not started |
| 4 | lsq_flood, local_tx_submission_garbage |
not started |
Goals
- Move the adversary from one-shot to long-running. ✅ Done in PR #99. The daemon now listens on a UNIX control socket, composer drivers fire one NDJSON request per Antithesis tick.
- Cover more of the protocol surface than chain-sync. In
progress:
chain_sync_flaplives; thrash + slow-loris next; then block-fetch, tx-submission, keep-alive, handshake, and upstream (responder). - Steer adversarial choices with the Antithesis hypervisor. ✅
In place since PR #103. Every randomness draw routes through the
RandomSourcetypeclass; default impl shells out toantithesis_randomwhen present and falls back toSystem.Randomotherwise. - Move the daemon home to
lambdasistemi/cardano-node-clients. ✅ Done. The package lives next tocardano-tx-generatorand shares itsProvider/Submitter/TxBuildlibrary surface plus the same release flow.
Architecture
+---------------------------------+
| cardano-adversary daemon |
| (Haskell, in cardano-node- |
| clients) |
| |
composer | - per-request N2N initiator |
parallel_ | connections to producers |
driver_*.sh --->| - reads chain points from |
over NDJSON | tracer-sidecar's file |
on UNIX socket | - exposes /state/adversary- |
| control.sock |
| - one request → one |
| adversarial action |
+---------------------------------+
Mapping from tx-generator (now established)
| tx-generator | cardano-adversary |
|---|---|
cardano-tx-generator daemon |
cardano-adversary daemon |
/state/tx-generator-control.sock |
/state/adversary-control.sock |
parallel_driver_transact.sh / _refill.sh |
parallel_driver_flaky_chain_sync.sh (Tier 1.1) |
eventually_population_grew.sh |
(Tier 2 will add an eventually_adversary_active.sh) |
finally_pressure_summary.sh |
(Tier 2 will add a finally_adversary_summary.sh) |
Wire spec at specs/034-cardano-tx-generator/contracts/control-wire.md |
Wire spec at specs/036-cardano-adversary/contracts/control-wire.md |
Implementation seams that exist today
Cardano.Node.Client.Adversary.{Application,ChainSync.Codec,ChainSync.Connection,ChainPoints}— the lifted N2N initiator library that backschain_sync_flapand will back the rest of Tier 1.Cardano.Node.Client.Adversary.RandomSource— typeclass + Antithesis-aware default (antithesis_randomCLI when present,System.Randomfallback). Per-request determinism viasplitFromSeed :: Word64 -> StdGen.Cardano.Node.Client.Adversary.Server— NDJSON dispatcher. New endpoints register themselves by extendingServerHooks.Cardano.Node.Client.Provider/Submitter/TxBuild— available for the N2C-side adversaries (mempool flooding, malformed tx submission) when Tier 2/4 lands.- A responder-library half for upstream-peer mode is not in the tree yet; it arrives with Tier 3.
Tier list of misbehaviour archetypes
Each archetype = one daemon endpoint + one composer
parallel_driver_*.sh + a documented invariant the cluster must
preserve under it. Order is implementation order, easiest first.
Tier 1 — port the existing surface into the daemon
chain_sync_flap✅ — pick random intersection point, syncLIMITblocks, disconnect. Replaces the stand-aloneadversarybinary that lived incardano-foundation/cardano-node-antithesis.chain_sync_thrash— same connection, repeatedlyMsgFindIntersectto a different random point without completing the sync. Stresses the producer's intersection-finding cache. Spec lives inlambdasistemi/cardano-node-clients/specs/036-cardano-adversary-thrash/.chain_sync_slow_loris— open the connection, complete handshake, then sendMsgRequestNextat a deliberately slow cadence; assert the connection is kept alive (or assert it gets evicted, whichever the protocol promises).
Tier 2 — other mini-protocols, downstream side
block_fetch_replay—BlockFetchclient that requests already-fetched ranges back-to-back, plus ranges straddling rollback boundaries.tx_submission_flood— N2NTxSubmission2client that announces tx-ids the producer has not requested, or refuses to deliver bodies after announcing them.tx_submission_garbage— submits well-formed-CBOR but ledger-invalid txs at high rate (mempool pressure).keepalive_abuse—KeepAlivecookies out of order or never replied to.
Tier 3 — upstream-peer mode (the canonical Byzantine peer)
Tracked as its own epic (#91). Cluster nodes must dial the adversary, so the adversary becomes a node-to-node server. Then:
upstream_fork_serve— announce a tip on the honest chain's history butMsgRollForwardwith a fabricated header. ChainSel must discard.upstream_equivocate— serve two contradictory headers at the same slot to two peers.upstream_too_far_ahead— announce a tip 10× past the real tip; producer must not fetch.upstream_long_rollback— induce a rollback past k and verify the honest peer rejects.
Tier 3 needs a topology change (relays peer with adversary in their
topology.json). That is a separate testnet variant; it does not
disturb the current cardano_node_master testnet.
Tier 4 — N2C abuse (lower priority)
lsq_flood— open manyLocalStateQuerysessions against a relay, hold them.local_tx_submission_garbage— N2C variant of #6.
Sequenced PR plan
| # | Repo | Description | Status |
|---|---|---|---|
| A | antithesis | Refresh docs/components/adversary.md, publish this roadmap |
✅ #88 |
| B | clients | Scaffold cardano-adversary daemon |
✅ #103 |
| C | clients | chain_sync_flap endpoint (port from antithesis-side) |
✅ #106 |
| D | antithesis | Switch components/adversary/ to consume the new image; delete the standalone binary |
✅ #99 |
| E | clients | chain_sync_thrash endpoint (Tier 1.2) |
spec written, impl next |
| F | clients | chain_sync_slow_loris endpoint (Tier 1.3) |
not started |
After Tier 1 is fully green on Antithesis: start Tier 2 issues, one endpoint per PR. Tier 3 waits on appetite for the topology variant.
Tickets
Filed in cardano-foundation/cardano-node-antithesis:
- ✅ #87 — refresh adversary docs (closed by PR #88).
- #89 — adversary roadmap epic (still open; tracks the rest of Tier 1+).
- ✅ #90 — switch to consuming the upstream daemon image (closed by PR #99).
- #91 — Tier 3 upstream-peer epic.
Filed in lambdasistemi/cardano-node-clients: