Assurance

Formal verification

The rules the covenant enforces are proved correct in Lean 4: 72 machine-checked theorems, no unfinished proofs, about 4,500 lines with no external libraries. The central one says that anyone holding a leaf and its path can always build an accepted exit that pays the leaf's amount, without anyone's help and without touching another leaf. The off-chain protocol, where the risks are orderings and timing, has a P model designed but not yet built.

Three layers, three kinds of check

A proof about the rules says nothing about whether the scripts implement them, and a test of the scripts says nothing about every possible tree. So each layer is checked by the method that fits it, and the layers are tied together by shared data.

What checks what. Only the first row is a proof; the others are execution and comparison.
LayerQuestionMethodStatus
Transition rules (spec 5, 7, 10.2)Do the rules conserve value, bind the right leaf and always allow an exit, for every depth and every tree?Lean 4 proofs, formal/72 theorems, axiom audit clean
Scripts (spec 8)Does each script accept exactly the spends its rule allows?A tapscript simulator runs every script on real trees with positive and negative cases7,690 checks across the four tiers, the channel leaves and the Merkle-sum measurement
Go implementationDo the Go scripts and transactions match the reference?Byte-for-byte golden vectors, and every Go-built witness replayed through the simulator145 golden scripts; 427 end-to-end spends, 0 failures
Off-chain protocol (spec 9, Appendix C)Do honest parties stay safe under every message ordering, reorg and adversarial choice?Model checking in PDesigned in design/p-model.md, not built

The headline theorem

Section 10.2 of the specification argues that a member can always leave. In Lean that argument is payable: for any tree and any position holding a leaf, the honest witness is a valid step from the current root to the root of the tree without that leaf, the sum drops by exactly the leaf's amount, and no other position changes.

theorem payable {d : Nat} (t : Tree d) (p : Pos d 0) (l : Leaf)
    (hl : t.get p = some l) :
    (leafStep (t.path p) (some l) none).valid t.root (t.set p none).root ∧
    (t.set p none).root.sum + l.amount = t.root.sum ∧
    ∀ q, q ≠ p → (t.set p none).get q = t.get q

Paired with ExitK.no_inflation, which says no exit pays out more than the tree holds, and exit_available, which says the exit pays the leaf's own exit key, this is the property the whole design exists for. chain_state lifts it to histories: every root that ever appears on-chain is the root of a tree reachable by the rules, its committed sum is that tree's total, and every leaf in it has an accepted exit.

What is proved

Conservation
Steps.sum: for any sequence of steps, the new root's sum plus what was removed equals the old root's sum plus what was added. It needs no assumption about the nodes, so the covenant conserves value even against a prover who invents nodes. Reachable.ledger states the same over a whole history.
Binding
fold_sound: any node and path that fold to the committed root are a real subtree and its real path. A forged leaf or a stale root cannot pass. This is the only theorem that uses the hash assumptions. witness_root_binds and output_binds tie the root to the output key, and CCVTweak.binding proves the same for the CCV tier's key from its two tweak stages.
Every transition
Exit, join, update, claim and split, merge and reclaim, the cohort sweep, the cohort rewrite and the detach each have a soundness theorem: what the old tree held, what output 0 commits to, and how the amounts balance. For example rewrite_payout_conservation covers a rewrite that mixes exits, joins, resizes and unchanged positions.
Vouchers
voucher_no_replay: once redeemed, a voucher's message never verifies against the tree again, under three stated hypotheses about issuers. voucher_survives_topup: an operator raising the leaf's amount does not void a voucher. voucher_survives_detach: a voucher follows its leaf into a detached tree.
The pending-leaf receive
The merge claim, the reclaim and the reclaim form of the cohort rewrite (payments) each have a soundness theorem: merge_sound says a merge moves value only from the source into the sibling it exits to, reclaim_sound that a reclaim removes one reclaimable leaf past its deadline and no other, and rewrite_transfer_sound that a rewrite can lower a reclaimable leaf only into its own sibling. reclaim_only_flagged says every leaf that is not flagged reclaimable stays in the tree, and a tree without a reclaim key accepts no reclaim at all. merge_body_preserved: a merge changes no leaf's body, so it voids no voucher. merge_target_monotone and merge_stale_rejected: a merge raises its target exactly to the voucher's level, and once the target holds that level, replaying the voucher or redeeming an older, lower one moves nothing. merge_no_overpay bounds what merges into one leaf can pay, along any history, by the largest level ever signed for it. rewrite_reclaimable_change: a reclaimable leaf changes without its owner only by an amount increase, an exactly absorbed transfer, or after its deadline.
Histories
Accepts.sound and Chain.sound bridge the chain and the model: every root a chain of accepted spends reaches is the root of a tree reachable by the transition records. Reachable.ledger keeps the books across those records, merges moving nothing and reclaims removing exactly the reclaimed amount. Reachable.dust and Reachable.evictable carry the dust floor and eviction through a whole history.
Eviction cannot be escaped
claim_evictable and Reachable.evictable: no sequence of carves produces a leaf the cohort's sweep cannot remove.
Path maintenance
path_maintenance: after any transition, every other member's path changes in exactly one entry, which they can compute from the published witness. Staying ready to exit needs no one's help.
Encodings
The leaf and node serialisations are injective, the 8-byte sum encoding is the minimal one the scripts compute, and the PADZ gadget of the MID and MIN tiers accepts exactly one pad and caps sums below 231.

What the proofs changed

Writing the proofs found places where the specification was wrong, imprecise or relying on something it did not say. Each became a fix, a stated requirement or a test.

What it assumes

Every theorem holds for any hash functions with these properties; the right column is how a deployment provides them.
AssumptionMeaningProvided by
Hash injectivityThe leaf and node hashes never collideSHA-256 collision resistance, plus the proved injectivity of the serialisations
Tag separationLeaf hashes, node hashes and the empty node never coincideDistinct BIP-340 tags
Output bindingThe tree's output key determines its root and script setDiscrete log on secp256k1 and collision resistance of the tweak hashes; for CCV, derived from the two tweak stages

What it does not prove

The off-chain protocol: a P model, designed

The off-chain failures are interleavings: an eviction landing between an HTLC's acceptance and its expiry, an operator's transaction replacing an owner's exit one block before a deadline, a reorg reverting an exit after a channel's update chain started. A model checker explores those orderings; no theorem about hash trees reaches them. design/p-model.md designs a model in P with seven machine types, among them the chain, an honest and a Byzantine operator, owners, channel parties and a watchtower, and these properties:

Planned properties. P checks safety by assertion and liveness under a fairness assumption on the scheduler.
#PropertyKind
P1An owner with a leaf and a path can always build an exit the covenant acceptssafety
P2A channel counterparty's balance is never payable to the owner alonesafety
P3No HTLC is accepted or left outstanding without the exit budget before its expirysafety
P4A signature for one channel and epoch never verifies for anothersafety
P5Eviction and sweeps pay every leaf exactly its amount, to its exit keysafety
P6The settlement that confirms is the latest one both parties signedsafety
L1Every honest exit eventually confirms despite a Byzantine operatorliveness
L2A force close settles within its delay plus a bounded number of contestsbounded liveness

The design includes six adversarial scenarios, one of them a fee-reserve exhaustion that is expected to fail, and a requirement that the model's covenant check is differentially tested against the Python reference so it cannot drift into checking a protocol nobody wrote. It is estimated at about seven weeks of work and has not been started. There is no TLA+ or PlusCal model.

Running it

cd formal
lake build    # compiles every module, then prints the axiom audit

The toolchain is pinned (Lean 4.24.0), there are no dependencies to fetch, and the build takes a few seconds. The audit prints the axioms each headline theorem depends on; only Lean's standard ones appear, and any line naming sorryAx would mean an unfinished proof. CI runs the build on every push.

Deeper