Formal Verification in PCN

How TLA+, Why3, Spin, and cryptographic proofs are used to verify Payment Channel Network protocols — turning informal specs into machine-checked guarantees.

TLA+ Why3 Spin/Promela UC Framework Fabiański NDSS 2025 Sprites Safety & Liveness
← Back to PCN Wiki Index
1

Why Formal Verification for PCN?

The stakes are high. Bitcoin locked in payment channels represents real money. A single bug in the protocol logic — not the cryptography — can be exploited by an adversary to drain all funds. Unlike a web app bug, there is no patch that recovers stolen satoshis.

What formal verification gives you

Safety

"Bad things never happen." Funds are never stolen; balances are always consistent; no honest party can be defrauded.

Liveness

"Good things eventually happen." Payments eventually complete or fail — they are never stuck in limbo forever, regardless of network conditions.

Security Proofs

"Even with a malicious adversary, the protocol achieves its goals." Cryptographic reduction to hard problems or ideal functionalities.

Bug taxonomy — what can go wrong, and what catches it

Race Condition

Two parties simultaneously try to resolve an HTLC. One message is processed before the other in an unexpected order, leaving funds in limbo or double-counted.

TLA+ Spin
🔒

Deadlock / Liveness Violation

A valid payment path cannot complete because timeouts are ordered incorrectly across hops. Every participant waits for another to act first.

TLA+ Spin (LTL)
⚖️

Invariant Violation

A sequence of valid-looking state transitions leads to a state where balances do not sum to the channel total, allowing one party to claim more than they own.

TLA+ Why3
🎭

Authentication Failure

A routing node can inject or modify an HTLC without being detected, or a revocation secret can be replayed to steal funds from a channel update.

Tamarin EasyCrypt
🧩

Compositionality Break

A protocol that is safe in isolation becomes unsafe when composed with concurrent payments across multiple channels — wormhole attack, balance correlation.

UC Framework EasyCrypt
🔍

Spec Ambiguity

The BOLT specification does not fully define behaviour in an edge case; different implementations diverge, one of which has exploitable behaviour.

Why3 TLA+

Three levels of formalization

1

Protocol-level

Model the state machine. Check all reachable states for safety invariants and liveness properties. Tools: TLA+, Spin.

2

Implementation-level

Verify actual code against a formal specification. Inductive proof that every code transition preserves invariants. Tools: Why3, Coq, Isabelle.

3

Cryptographic

Prove security against computationally bounded adversaries. Reduction to hard problems or UC ideal functionalities. Tools: EasyCrypt, Tamarin.

Real PCN bugs formal methods could have caught

HTLC resolution race: If Alice broadcasts HTLC_FAIL and Bob simultaneously sends HTLC_FULFILL, commitment state can diverge — funds get stuck. TLA+/Spin enumerate this interleaving.

Timeout ordering: In a 3-hop route, if T_BC ≥ T_AB, Bob can get the preimage from Carol without being able to claim from Alice. TLA+ liveness check catches this immediately.

Revocation replay: An old commitment transaction published by a malicious party must always be claimable with the revocation key. Tamarin verifies this authentication invariant holds under all adversary strategies.

2

TLA+ for Lightning Network State Machines

What is TLA+?

Leslie Lamport's Temporal Logic of Actions — a specification language for concurrent and distributed systems. You write state variables, initial conditions, actions (transitions), and invariants. The TLC model checker exhaustively explores all reachable states and verifies that every invariant holds.

Why TLA+ for PCN?

Payment channels are concurrent state machines: two parties exchange messages asynchronously. TLA+ excels at finding unexpected interleavings — the exact scenario that causes a real bug. TLC can check millions of states automatically with no human proof effort.

Lightning Network as TLA+ — simplified pseudospec

(* State variables *)
VARIABLES alice_balance, bob_balance, htlc_in_flight, state,
          preimage_revealed, timeout_expired

(* Initial condition: channel opened with TOTAL_FUNDS split equally *)
Init ==
  /\ alice_balance = TOTAL_FUNDS \div 2
  /\ bob_balance   = TOTAL_FUNDS \div 2
  /\ htlc_in_flight = 0
  /\ state = "open"
  /\ preimage_revealed = FALSE
  /\ timeout_expired   = FALSE

(* Action: Alice proposes an HTLC *)
HTLC_SETUP(amount) ==
  /\ state = "open"
  /\ alice_balance >= amount
  /\ htlc_in_flight' = amount
  /\ alice_balance'  = alice_balance - amount
  /\ state' = "htlc_pending"
  /\ UNCHANGED <<bob_balance, preimage_revealed, timeout_expired>>

(* Action: Bob reveals preimage, receives funds *)
HTLC_FULFILL ==
  /\ state = "htlc_pending"
  /\ preimage_revealed = TRUE
  /\ htlc_in_flight' = 0
  /\ bob_balance'    = bob_balance + htlc_in_flight
  /\ state' = "settled"
  /\ UNCHANGED <<alice_balance, preimage_revealed, timeout_expired>>

(* Action: HTLC expires, Alice reclaims funds *)
HTLC_FAIL ==
  /\ state = "htlc_pending"
  /\ timeout_expired = TRUE
  /\ htlc_in_flight'  = 0
  /\ alice_balance'   = alice_balance + htlc_in_flight
  /\ state' = "failed"
  /\ UNCHANGED <<bob_balance, preimage_revealed, timeout_expired>>

(* Safety invariant — checked after EVERY transition *)
BalanceInvariant ==
  alice_balance + bob_balance + htlc_in_flight = TOTAL_FUNDS

(* Liveness: payment must eventually resolve *)
PaymentLiveness ==
  state = "htlc_pending" ~> (state = "settled" \/ state = "failed")
Core safety invariant (holds in every reachable state):
alice_balance + bob_balance + htlc_in_flight = TOTAL_FUNDS
TLC verified: invariant holds across all 2N state interleavings for N concurrent HTLCs

Interactive state machine — click a state to explore

Open
HTLC
Pending
Settled
Failed
Disputed

Select a state above

Click any state node to see transitions, invariant status, and what variables change.

What TLC finds automatically

TLC explores every possible interleaving of concurrent actions. For example: if Alice sends HTLC_FAIL and Bob simultaneously sends HTLC_FULFILL, TLC identifies the exact sequence — and which one executes first — that could violate the balance invariant. In practice this found 3 ambiguous cases in the BOLT spec (see §3).

3

Paper — Fabiański et al., NDSS 2025 NDSS 2025

"Formal Analysis of the Lightning Network's Security"
Fabiański, Milewski, Malavolta, Aumayr · NDSS 2025 · First machine-checked formal verification of the full LN protocol
Key contribution: First formal, machine-checked proof covering the complete Lightning Network protocol — not a toy model, but the full commitment transaction cycle including HTLCs, revocations, and fee negotiation.
Tool: Why3 — functional deductive verification tool with an ML-like specification language. Proofs discharged by SMT solvers (Z3, Alt-Ergo) and the Coq backend.
Key discovery: 3 subtle corner cases in the BOLT specification that are technically underspecified (not exploitable in practice, but formally ambiguous). Led to official BOLT clarifications.

What they verified — click each layer for details

4

Security Properties

Balance security · Payment atomicity · Conditional liveness

Balance security: At any point in time, Alice's actual claimable balance on-chain ≥ her expected channel balance. This holds even if Bob broadcasts any of his old commitment transactions.

Payment atomicity: A payment either completes fully (preimage revealed and credited) or reverts fully (HTLC failed and funds returned). No partial states are reachable.

Liveness (conditional): If all parties are honest and the network is cooperative, a payment eventually succeeds. Requires that the Bitcoin mempool does not censor transactions — formally stated as a fairness assumption.
3

Protocol Model

update_add_htlc · commitment_signed · revoke_and_ack · update_fulfill_htlc

Full BOLT #2 message sequence formally specified in Why3. Each message handler is a function from (channel state, message) → (new channel state, output messages). The spec covers:

• The full commitment transaction signing ceremony (both directions)
• HTLC addition, fulfillment, and failure with proper state tracking
• Revocation key derivation and the penalty transaction mechanism
• Fee negotiation and dust threshold handling

The model has ~4,200 lines of Why3 specification.
2

Transaction Model

Funding tx · Commitment tx · HTLC-Success tx · HTLC-Timeout tx · Justice tx

Each transaction type is modeled as a record with inputs, outputs, lock times, and witness scripts. The model captures:

• Funding transaction: 2-of-2 multisig output
• Commitment transactions: asymmetric (Alice's vs Bob's), to_local with CSV, to_remote with CLTV
• HTLC-Success and HTLC-Timeout: spending paths from commitment HTLC outputs
• Justice (penalty) transaction: spending revoked commitment output with revocation key

Bitcoin Script semantics are axiomatized — the model trusts that Bitcoin correctly validates scripts.
1

Bitcoin Script Semantics

Hash preimage · CSV · CLTV · Multisig · Revocation

Base axioms for script execution:

OP_CHECKLOCKTIMEVERIFY (CLTV): output unspendable before absolute block height N
OP_CHECKSEQUENCEVERIFY (CSV): output unspendable before N blocks after confirmation
OP_SHA256 / OP_HASH160: hash preimage witness requirements
• Multisig: both parties must sign; revocation key invalidates this requirement

These are not proven from Bitcoin source code — they are trusted axioms. Bridging this gap to Bitcoin Core's script interpreter is an open problem.

Proof structure — click any node to expand

Balance Security Theorem machine-checked
Every honest party's claimable balance on-chain is always ≥ their off-chain expected balance, regardless of what the malicious counterparty broadcasts.
Commitment Tx Balance Lemma proven
The latest commitment transaction always encodes the correct balance for both parties. Proven by induction over the update sequence.
Revocation Completeness Lemma proven
For every old commitment transaction that the adversary might publish, the honest party holds a valid justice (penalty) transaction that can reclaim all funds. Requires: revocation secret was correctly generated and stored.
HTLC Resolution Lemma proven
Every in-flight HTLC resolves correctly: either the preimage is revealed and funds credited to Bob, or the timeout elapses and funds returned to Alice. The two paths are mutually exclusive.
Payment Atomicity Theorem machine-checked
A multi-hop payment either completes fully (all HTLCs fulfilled) or reverts fully (all HTLCs failed). There is no reachable state where some hops are fulfilled and others are not — the HTLC cascade ensures this.
Preimage Propagation Lemma proven
Once a preimage is revealed at the last hop, it propagates backwards through each intermediate hop within the timeout window. Each hop has sufficient time to claim its HTLC before the upstream timeout.
Liveness (Conditional) fairness assumption
If all parties are honest, messages are delivered, and Bitcoin transactions are eventually mined (fairness), then a payment eventually terminates. Cannot be proven unconditionally due to Bitcoin's best-effort transaction inclusion.
4

Model Checking with Spin / Promela

What is Spin?

Explicit-state model checker by Gerard Holzmann. Uses the Promela language — more imperative than TLA+, with explicit message channels and concurrent processes. Spin exhaustively verifies LTL (Linear Temporal Logic) properties over all reachable states.

Promela vs TLA+

Spin/Promela: Imperative style, message-passing channels, natural for protocol simulations. Better for modelling concurrent processes exchanging typed messages.

TLA+: Mathematical/relational style, state transitions as logical formulas. Better for abstract state machine properties.

Lightning channel in Promela — conceptual model

/* Message types */
mtype = { UPDATE_ADD_HTLC, COMMITMENT_SIGNED, REVOKE_AND_ACK,
          UPDATE_FULFILL_HTLC, UPDATE_FAIL_HTLC, ERROR }

/* Shared channel state */
int alice_balance = 500000;  /* satoshis */
int bob_balance   = 500000;
int htlc_amount   = 0;
byte channel_state = 0;     /* 0=open, 1=pending, 2=closed */

/* Bidirectional channel between Alice and Bob */
chan a2b = [4] of { mtype, int };
chan b2a = [4] of { mtype, int };

active proctype Alice() {
  int amount;
  do
  :: /* Initiate payment */
     amount = 10000;
     atomic {
       alice_balance >= amount;
       alice_balance = alice_balance - amount;
       htlc_amount = amount;
       channel_state = 1;
     }
     a2b!UPDATE_ADD_HTLC, amount;
     b2a?COMMITMENT_SIGNED, _;
     a2b!REVOKE_AND_ACK, 0;
     b2a?COMMITMENT_SIGNED, _;
     a2b!REVOKE_AND_ACK, 0;
     /* Await resolution */
     if
     :: b2a?UPDATE_FULFILL_HTLC, _ ->
           skip   /* payment succeeded */
     :: b2a?UPDATE_FAIL_HTLC, _ ->
           alice_balance = alice_balance + amount;
           htlc_amount = 0;
           channel_state = 0;
     fi
  :: break
  od
}

active proctype Bob() {
  int amount;
  do
  :: /* Respond to incoming HTLC */
     a2b?UPDATE_ADD_HTLC, amount;
     b2a!COMMITMENT_SIGNED, 0;
     a2b?REVOKE_AND_ACK, _;
     b2a!COMMITMENT_SIGNED, 0;
     a2b?REVOKE_AND_ACK, _;
     if
     :: /* Preimage known — fulfill */
        b2a!UPDATE_FULFILL_HTLC, 0;
        bob_balance = bob_balance + amount;
        htlc_amount = 0; channel_state = 0;
     :: /* Cannot fulfill — fail */
        b2a!UPDATE_FAIL_HTLC, 0;
     fi
  :: break
  od
}

/* Never property: Spin verifies this never becomes false */
never { /* balance sum invariant */
  do
  :: skip
  :: (alice_balance + bob_balance + htlc_amount != 1000000) -> break
  od
}

LTL Properties — hover or click for explanation

[] (alice_balance + bob_balance + htlc_in_flight = TOTAL)
Always: the sum of all balances equals the total channel capacity
Type: Safety property ([] = "globally always")
What it catches: Any state transition that causes funds to appear or disappear. If the model checker finds a counterexample, there is a sequence of messages that results in money creation or destruction.
Status in LN: ✓ Verified — holds for all reachable states in the Fabiański et al. model.
<> (payment_complete || payment_failed)
Eventually: every payment resolves (either succeeds or fails — never stuck)
Type: Liveness property (<> = "eventually")
What it catches: Payment deadlocks. If this formula is violated, Spin produces a counterexample trace showing the exact sequence of actions that leads to a state where neither payment_complete nor payment_failed is ever reached.
Status in LN: ⚠ Conditional — holds only under fairness assumption (Bitcoin transactions eventually confirmed). Violated if a counterparty can censor mempool indefinitely.
Counterexample: HTLC timeout T=100, Bob reveals preimage at block 99, Alice's reclaim tx stuck in mempool. Both parties have valid conflicting claims for blocks 99-100.
[] (old_state_broadcast -> <> justice_tx_published)
Always: if an old commitment state is broadcast, a justice transaction will eventually be published
Type: Safety + Liveness (combined)
What it catches: Failures in the watchtower / revocation mechanism. If a malicious party publishes an old commitment, the honest party (or their watchtower) must respond within the CSV timelock window.
Status in LN: ✓ Verified by PISA paper (watchtower protocol). Requires the watchtower is online and has the revocation data.
[] (channel_open -> <> (channel_closed_cooperatively || channel_closed_force))
Always: every open channel eventually closes (no channel is permanently stuck open)
Type: Liveness property
What it catches: Channel griefing — a counterparty who neither cooperates with closing nor triggers unilateral close. In practice this is a griefing attack: attacker keeps the channel open to waste the honest party's capital.
Status in LN: ✓ Holds — any party can force-close unilaterally at any time by broadcasting their latest commitment transaction.
[] (htlc_in_flight > 0 -> <> htlc_in_flight = 0)
Always: if an HTLC is in flight, it will eventually resolve to zero (not accumulate forever)
Type: Liveness property
What it catches: HTLC accumulation attacks. If an attacker can keep HTLCs in-flight indefinitely without triggering a timeout, they can exhaust the channel's HTLC slot capacity (max 483 per BOLT spec), making the channel unusable.
Status in LN: ✓ Holds — HTLC timeouts are enforced on-chain if counterparty is unresponsive.
5

Cryptographic Security Proofs — UC Framework

Universal Composability (UC) (Canetti 2001): a protocol P is secure if any adversary attacking P can be "simulated" against an ideal functionality F — a trusted third party that does exactly what we want. Security is preserved even when P is composed with other protocols running concurrently.

Ideal functionality F_LN

/* The "God's-eye" ideal version of LN */

On receive (OPEN, Alice, Bob, amount):
  channel[Alice,Bob] = {
    bal_a: amount / 2,
    bal_b: amount / 2,
    open: true
  }

On receive (PAY, from=Alice, to=Bob, amt):
  if channel[Alice,Bob].bal_a >= amt:
    channel[Alice,Bob].bal_a -= amt
    channel[Alice,Bob].bal_b += amt
    output (SUCCESS, Bob, amt)
  else:
    output (FAILURE, Alice)

On receive (CLOSE, Alice, Bob):
  pay channel[Alice,Bob].bal_a to Alice on-chain
  pay channel[Alice,Bob].bal_b to Bob on-chain
  channel[Alice,Bob].open = false

/* Real LN ≡_UC F_LN iff:
   for all env Z, adv A, ∃ sim S:
   EXEC[LN, A, Z] ≈ EXEC[F_LN, S, Z] */

UC Proof components

Environment Z

Adaptive adversary that controls all inputs and observes outputs

distinguishes
real vs ideal

Adversary A / Simulator S

Corrupts parties, controls the network, observes messages

Real Protocol LN

Actual HTLC messages, Bitcoin transactions, timeouts

UC

Ideal Func F_LN

Trusted party: instant, atomic, correct balance updates

Click a component to learn its role
The UC proof shows: anything the adversary can do in the real protocol, the simulator can reproduce against the ideal functionality — so the environment cannot distinguish them.

Malavolta et al. — Concurrency and Privacy in PCN

"Concurrency and Privacy with Payment-Channel Networks"
Malavolta, Moreno-Sanchez, Kate, Maffei, Ravi · CCS 2017
Proved: HTLC-based PCN achieves balance security under concurrent payments — even when multiple payments are in-flight simultaneously across the same channel.
Critical finding: Naive PCN (without payment amount randomization per hop) is not secure under concurrency. An observer who controls two routing nodes can correlate HTLC amounts to de-anonymize a payment path.
Fix (AMHL): Randomize HTLC amounts at each hop using an anonymous multi-hop lock. Each hop adds a random blinding factor so that amount_alice ≠ amount_bob ≠ amount_carol from any single observer's view.

Key security properties — click to expand

Balance Security

∀t. honest_balance(t) ≥ committed_balance(t)

Honest party always gets at least what they are owed.

An honest party's claimable balance on the Bitcoin blockchain is always at least their expected off-chain balance. This holds for all time steps t, even if the counterparty is malicious and broadcasts arbitrary transactions.

Proof strategy: Show that for every adversarial strategy that tries to reduce the honest party's balance, the honest party can broadcast a transaction (justice tx or HTLC claim tx) that restores or exceeds their balance.

Value Atomicity

∀p. (∃i. htlc_i(p).fulfilled) → (∀j. htlc_j(p).fulfilled)

Either all HTLCs in a payment resolve, or none do.

For any payment p with HTLCs indexed i, j, ...: if any one HTLC is fulfilled (preimage revealed), then all HTLCs in the same payment must be fulfilled. This prevents partial payments where some hops are credited but others are not.

Proof strategy: All HTLCs in a payment share the same preimage hash. Once one hop reveals the preimage, every other hop can use it. The cascade is enforced by the timeout ordering (T_last < T_last-1 < ... < T_first).

Relationship Anonymity

∀A. Pr[A identifies (sender,recv)] ≤ 1/|paths| + negl(λ)

Routing nodes cannot identify sender/receiver from HTLC data alone.

A network-level adversary controlling all intermediate routing nodes cannot determine the true sender and receiver of a payment with probability better than random guessing over the set of possible paths, plus a negligible term in the security parameter λ.

Proof strategy: Uses the UC simulation argument. The simulator constructs HTLC amounts and hashes that are indistinguishable from random, so the adversary's view is computationally independent of the true sender/receiver identity.

Caveat: This holds for the cryptographic level. Timing analysis and balance correlation attacks are separate threat models.

Griefing Resistance

∀A∃T. payment_completes_by(T) ∨ funds_returned_by(T)

Attacker cannot indefinitely delay an honest party.

For any adversarial strategy A, there exists a time bound T such that the honest party either receives their payment or recovers their funds by time T. This prevents the griefing attack where an attacker keeps HTLCs in-flight forever to lock up capital.

Proof strategy: The HTLC timeout mechanism guarantees a hard deadline. Even if the counterparty is unresponsive, the honest party can broadcast the HTLC-timeout transaction after the CSV/CLTV expires.

Limitation: T depends on block confirmation time. If the Bitcoin mempool is congested, T may be larger than expected — this is the basis of fee-siphoning attacks under congestion.
6

Paper — Sprites (Miller et al., WWW 2019)

"Sprites and State Channels: Payment Networks that Go Faster"
Miller, Bentov, Bakshi, Kumaresan, McCorry · WWW 2019
Key contribution: Introduced the formal concept of payment deadlock and proved it is inherent in any HTLC-based PCN. Proposed "Sprites" — a smart-contract-based replacement that reduces collateral lockup from O(N × T) to O(T).

The HTLC timeout ordering constraint

Why timeouts must be staggered

In a 3-hop route Alice → Bob → Carol:

Alice→Bob: HTLC timeout at block TAB = 200
Bob→Carol: HTLC timeout at block TBC = 150

Constraint: TBC < TAB is mandatory for Bob's safety. If Carol reveals the preimage at block 149, Bob must be able to claim the Alice→Bob HTLC before block 200. Bob needs TAB - TBC ≥ δ blocks to get his transaction confirmed.

Consequence: With N hops and timeout T per hop, total collateral lockup = N × T blocks. For N=20 hops and T=144 blocks (1 day): 20 days of locked capital.

Sprites solution

Sprites Mechanism

Replace per-hop HTLC timeouts with a single global smart contract ("PreimageManager"). Each hop locks funds in the contract with a single global expiry time T.

Once the preimage is submitted to the contract, all hops can claim simultaneously — no cascade needed. Total lockup: O(T) regardless of N.

Formal model result

Sprites formally proves: under their model, collateral efficiency is optimal — no HTLC-based protocol can do better than O(T) lockup per payment.

Limitation: requires a smart contract platform (Ethereum), not directly applicable to Bitcoin's script-limited environment without modifications.

Collateral lockup calculator

Standard HTLC lockup
720
Sprites lockup
144
7

Formal Verification Tools Comparison

Click any row to expand details about learning curve, strengths, limitations, and PCN papers that use each tool.

Tool Type Language What it verifies PCN papers
TLA+ / TLC
Model checking TLA+ State machine safety & liveness LN state machine, multi-hop atomicity
Spin
Model checking Promela Concurrent protocol LTL properties Channel protocols, HTLC ordering
Why3
Deductive verification WhyML Code correctness vs. spec Fabiański NDSS 2025
Coq
Proof assistant Gallina Mathematical theorems about protocols Perun framework, virtual channels
Tamarin
Protocol verifier Tamarin Cryptographic protocol properties Authentication, key agreement in LN
EasyCrypt
Crypto proof assistant EasyCrypt UC/game-based security proofs PCN privacy, AMHL security
8

Specification Coverage Dashboard

Which LN protocol components have been formally verified? Hover any cell for details.

Formally verified
🔶 Partially verified / under research
Not yet formally verified
Component TLA+ Why3 UC Proof Tamarin Notes

Open problems in formal verification

1. Full MPP atomicity proof under concurrency

Multi-Path Payments split a single payment across multiple channels. Proving that all sub-payments atomically succeed or fail — even under concurrent interference from other payments — remains an open problem. The interaction between payment shards and routing is complex.

2. Eltoo / LN-Symmetry formal security model

Eltoo (SIGHASH_ANYPREVOUT) removes the asymmetric revocation mechanism entirely. The security model fundamentally changes — instead of revocation keys, the protocol relies on update transactions superseding each other. No complete UC-security proof exists for Eltoo yet.

3. Route blinding privacy proof

Route blinding (BOLT 12) hides the recipient's node identity by encrypting the last hops. A formal privacy proof — showing the adversary cannot identify the recipient beyond a negligible advantage — has not been published. The interaction with onion routing makes this subtle.

4. Channel factory security under Byzantine participants

Channel factories allow N parties to share a single funding transaction. If any participant goes Byzantine (not just malicious but also offline), the safety and liveness of the remaining channels must be formally guaranteed. This requires a multi-party UC model that is significantly harder than 2-party channels.

5. Post-quantum PCN security proofs

Current PCN proofs assume hardness of ECDLP (for signatures) and collision-resistance of SHA-256 (for HTLC hashes). Post-quantum adaptor signatures change the security assumptions. No formal proof of a complete PQ-PCN exists.