Skip to main content

Model Syntax

Models define labeled transition systems. Nodes are opaque witness worlds; edge labels and predicates carry the contract meaning.

Basic Structure​

model <name> {
initial <node>

<from_node> -> <to_node> [+<label> +<predicate1> -<predicate2>]
}

Complete Example​

model escrow_witness {
initial q0

q0 -> q1 [+DEPOSIT +signed_by(/parties/buyer.id) +num_gte(/deposit/amount.num, /terms/price.num)]
q1 -> q2 [+DELIVER +signed_by(/parties/seller.id)]
q2 -> q3 [+RELEASE +signed_by(/parties/buyer.id)]
q1 -> q4 [+DISPUTE +signed_by(/parties/buyer.id)]
}

The names q0, q1, q2, and so on are not escrow phases. They identify nodes in a witness LTS so the verifier can evaluate modal formulas.

Nodes​

initial q0

initial names the starting witness node. Additional nodes are introduced by transitions. Prefer neutral names such as q0, q1, or generated ids unless you are writing a low-level debugging example.

Transitions​

// Basic labeled transition
q0 -> q1 [+ACTION_NAME]

// With predicates
q1 -> q2 [+ACTION_NAME +predicate1(args) -predicate2(args)]

// Multiple transitions with the same label are nondeterministic
q1 -> q2 [+DISPUTE]
q1 -> q3 [+DISPUTE +signed_by(/parties/arbiter.id)]

Parts​

model Contract {
part a {
q0 --> q1: +signed_by(/parties/alice.id)
}
part b {
q0 --> q2: +signed_by(/parties/bob.id)
}
}

Parts are one graph joined by node name. A commit at q0 may take an edge out of q0 in any part, so rules are checked against every part's edges together: [-signed_by(/parties/alice.id)] false fails here, because part b lets Bob commit alone. A node name in two parts is the same node.

A part of same-node loops allows commits that do not advance the rest of the model, such as notes or evidence, while the contract stays at its node:

model Contract {
part flow {
q0 --> q1: +signed_by(/parties/alice.id)
q1 --> q2: +signed_by(/parties/bob.id)
}
part notes {
q1 --> q1: +signed_by(/parties/alice.id) -modifies(/parties)
}
}

Rules range over these loops like any other edge, so give them the signer labels the rules require. A loop keeps the contract at q1 but may still write state; -modifies says which paths it leaves unchanged.

Comments​

// A comment takes a whole line.
model witness {
initial q0
// Comments may sit between transitions.
q0 -> q1 [+START]
}

Only whole-line // comments are supported. A comment after code on the same line, or a /* ... */ block, is a parse error.