txflow spec reference
A txflow spec describes a transaction-flow experiment: which transactions to build, which outputs each one spends, and where and when each one is sent. It never contains transaction ids, keys or amounts that depend on the chain; those are filled in when the experiment is built.
The field tables, pattern list, relays and limits on this page are generated from the same schema and configuration the editor checks against, so they cannot drift from what the site accepts.
Overview
A spec goes through four steps. The editor does the first two as you write; Run on Musashi does the last two.
- Check. The spec is validated against the JSON Schema (its shape) and then expanded (its meaning).
- Expand. Patterns become named funding outputs and transactions: the graph IR. Conflicts, orphans and a content hash are derived from which outputs each transaction spends; they are never declared.
- Materialise. Each transaction is built and signed on the server, funded from a lane of test ADA that the run leases.
- Submit. The schedule sends each transaction to its relays at its offset, and the run is watched until it settles; what is left is swept back to the lane.
A spec is built from four kinds of pieces:
- Atoms
- Funding outputs (
sources), transactions (txs) and their outputs. Every graph is made of these. - Patterns
- Named shapes that emit atoms: a chain, a fan-out, a double spend. Most specs are only patterns.
- Controls
params,countand pattern arguments that scale a shape without rewriting it.- Schedule
- Releases that send selected transactions to relays, at an offset and with spacing.
Your first experiment
Now that you know what a spec is made of, write one. You'll describe three transactions in a row, check the spec, and then turn it into a race between two continents.
Writing the spec
Make a file called chain.yaml and enter the spec in Listing 1.
version: 0
experiment: first-chain
seed: 1
wallets:
alice: {}
patterns:
- id: chain
use: chain
with: { length: 3 }
schedule:
releases:
- select: { pattern: chain }
target: frankfurt
Anatomy of a spec
Let's go through Listing 1 from the top.
The first line, version: 0, says which version of the format the spec is written in. Every spec starts with it, so an old spec can still be checked after the format moves on. experiment names the run, and seed makes everything derived from the spec, such as names and keys, come out the same each time it is built.
wallets lists who owns the coins. One wallet, alice, is enough for most experiments: every output pays back to her.
patterns is where the shape lives. This entry uses the chain pattern and names the instance chain. With length: 3 it emits a funding output chain/s and the transactions chain/t1, chain/t2 and chain/t3, each spending the one before.
schedule says where and when each transaction is sent. Its one release selects every transaction of the pattern and sends them to the Frankfurt relay. With no at, they go at once, in dependency order, so each parent reaches the relay before its child.
Checking it
Click Open in editor under Listing 1 to see its graph, or check it from a terminal:
$ txflow check --network networks/musashi.json chain.yaml
3 transactions funded by 1 source (10 ADA in total).
Pattern `chain` uses `chain`.
No conflicts: every output is spent at most once.
3 sends to 1 target:
frankfurt: 3 txs via n2c, at 0 ms
The check expands the patterns into the graph and reports what it found. A spec that passes is runnable: every name resolves, no path spends an output twice, and every send has a relay that takes it.
Racing two relays
Now add a double spend: two transactions that spend the same output, sent at the same moment to two continents. At most one of them can land. Listing 2 adds the conflict_set pattern and two releases; the new lines are highlighted.
version: 0
experiment: first-race
seed: 1
wallets:
alice: {}
patterns:
- id: chain
use: chain
with: { length: 3 }
- id: race
use: conflict_set
with: { k: 2 }
schedule:
releases:
- select: { pattern: chain }
target: frankfurt
- select: { names: [race/x1] }
target: frankfurt
- select: { names: [race/x2] }
target: tokyo
$ txflow check --network networks/musashi.json race.yaml
5 transactions funded by 2 sources (20 ADA in total).
Pattern `chain` uses `chain`.
Pattern `race` uses `conflict_set`.
1 conflict set (at most one tx of each can land):
race/x1, race/x2 all spend race/s
5 sends to 2 targets:
frankfurt: 4 txs via n2c, at 0 ms
tokyo: 1 tx via n2c, at 0 ms
The check now reports a conflict set: race/x1 and race/x2 both spend race/s. The conflict is never declared; it follows from what each transaction spends. Which side wins, and how fast the loser disappears from each mempool, is what this experiment measures.
Summary
- A spec is a version, a name, a seed, wallets, patterns and a schedule.
- Patterns emit named transactions; you refer to them as
pattern/name. - Conflicts come from what transactions spend, never from a flag.
- Releases send transactions to relays, parents first.
The rest of this page is the reference: every field, every pattern and every rule the check applies.
Example
Two chains whose transactions conflict pairwise, raced from two continents, and a three-way double spend with one copy sent to each relay at the same moment.
Loading…
Document
A spec is a YAML (or JSON) object with these fields. Unknown fields are errors.
Names and grammar
Everything is named, and transactions refer to each other only by name. Everything a pattern emits lives under its id: chains/a1. A pattern with count emits copies under <id>_0/, <id>_1/, and so on. .N after a transaction's name picks its output N, counting from 0.
name = lower , { lower | digit | "_" } ;
path = name , { "/" , name } ; (* chains/a1, p_2/s *)
ref = path , [ "." , index ] ; (* a source, or output index of a tx *)
index = "0" | nonzero , { digit } ; (* no leading zeros *)
label = ? any string ? ; (* free tag, e.g. side-a *)
arg = integer | '"' , integer , '"' | '"{{' , expr , '}}"' ;
expr = term , { ( "+" | "-" ) , term } ;
term = factor , { ( "*" | "/" ) , factor } ; (* "/" divides whole numbers *)
factor = integer | name | "(" , expr , ")" | "-" , factor ;
duration = "0" | digit , { digit } , ( "ms" | "s" ) ; (* at most one hour *)
amount = integer | "min" | "change" ; (* lovelace *)
lower = "a" | … | "z" ;
digit = "0" | … | "9" ;
nonzero = "1" | … | "9" ;
A name in expr is one of the spec's params. The result must be a whole number of at least 0, and is then held to the argument's range.
Patterns
Each entry of patterns is { id, use, count?, with? }. use names one of the shapes below; with sets its arguments, each of which may be a number or an expression over params.
Sources and transactions
For shapes no pattern covers, write funding outputs and transactions directly. They can spend pattern outputs by name, for example chains/a1.0.
Source
Transaction
Output
Schedule
The schedule says where and when each transaction is sent. Each release selects transactions and sends them to one relay. A transaction selected by several releases is sent by each, which is how one transaction reaches several relays; a transaction no release selects is not sent, and the check warns about it.
Release
Selecting transactions
Sends are ordered by time. Within one release, the selected transactions are sent in dependency order, spacing apart, starting at at.
A node accepts a transaction only when it already has every transaction it spends or reads from; it keeps no orphans. The check therefore warns when a transaction reaches a relay before one of its parents: if the parent has not been sent anywhere yet, the relay will reject it; if the parent went only to another relay, it lands only if propagation brings the parent there first. Send parents to the same relay earlier, or in the same release.
Options
Relays of this site
Validation
A spec is runnable when it passes all of these. The check reports the first problem it finds in each step, naming the pattern, transaction or field at fault.
- Shape. The document matches the schema: known fields only, names spelt as in the grammar, arguments within their ranges.
- Version.
versionis the version this site reads. - Unique names. Pattern ids are distinct, and no two sources or transactions end up with the same name.
- Wallets. Every owner is a declared wallet. With several wallets each pattern names its
wallet. - References resolve. Each input is a source, or output N of a transaction that has at least N+1 outputs. No transaction spends or reads its own output, or lists an input twice. There are no cycles.
- One change output. Every transaction has exactly one output whose amount is
change. - No double spend along a path. A transaction cannot depend on two transactions that spend the same output, and cannot read an output one of its ancestors spends: such a transaction could never land. Conflicts between independent branches are allowed, and are the point of many experiments.
- Schedule. Every release selects at least one transaction, targets a relay of this site's network that accepts its
via, and stays within one hour. - Limits. The expanded graph fits this site's limits (below).
Derived, never declared: a conflict set is two or more transactions spending the same output (at most one can land); a transaction is orphanable when a conflict among its ancestors can go the other way; reference contention is an output spent by one transaction and read by another.
Limits of this site
Limits are this site's policy, not part of the format: a spec beyond them is valid txflow, but this site will not run it.
Versioning
Every spec starts with version. Adding a pattern, an optional argument or a wider range keeps the version: every valid spec stays valid and means the same. Removing or renaming anything, narrowing a range, changing a default or changing what a spec expands to is a new version, and the schema of every earlier version stays published so older specs can still be checked.
Machine-readable: JSON Schema (draft 2020-12).