Without special cryptography, intermediate routing nodes can trivially identify that they are forwarding the same payment — breaking sender/receiver privacy.
Naive HTLC Route
Each hop receives the same payment hash H. Since H is deterministic and identical, any two colluding nodes can confirm they're on the same payment path.
Alice→BobH = a3f9…
Bob→CarolH = a3f9…
Carol→DaveH = a3f9…
Payment Correlation Attack
Bob and Carol collude
Both see hash H = a3f9…
Same H → same payment!
They infer: Alice → Dave, amount ~X sats
Sender and receiver deanonymized
Why this matters
Even with onion routing (source routing), the payment hash leaks correlation between hops. Any two nodes on the path — even non-adjacent — can link payments to a single sender.
Animated: Colluding Nodes Detect Correlation
Click the button to watch Bob and Carol compare notes and detect the same payment hash.
Alice→BobH = a3f9b2c1
Bob→CarolH = a3f9b2c1
Carol→DaveH = a3f9b2c1
🔍 Bob & Carol collude: "We both forwarded H = a3f9b2c1 — it's the same payment! Alice is paying Dave."
Key Problem
Payment hash H is a global identifier — it links all hops of a single payment. This is the fundamental privacy flaw that PTLC and Adaptor Signatures solve by making each hop's "lock value" unlinkable.
HTLC — Hash Time-Locked Contracts
Step through a 3-hop payment Alice → Bob → Carol → Dave. HTLC binds payment to a hash preimage with timelocks.
1
Setup: Alice creates the secret
AMHL — Anonymous Multi-Hop Locks
The key insight: give each hop a different lock value, but ensure a single key can satisfy all of them. This breaks correlation while preserving atomicity.
HTLC Problem: Same Lock Everywhere
Alice→Boblock = H
Bob→Carollock = H
Carol→Davelock = H
Three identical locks → trivially correlated.
AMHL Fix: Different Locks, Same Key Structure
Alice→Boblock = z₀
Bob→Carollock = z₁
Carol→Davelock = z₂
Three different locks → no correlation!
The Math: Additive Key Derivation
Alice picks secret scalar r and random blinding factors α₁, α₂.
z₀ = r ← Alice's secret (also claim key for last hop)
z₁ = r + α₁ ← Bob's lock (given to Bob encrypted)
z₂ = r + α₁ + α₂ ← Carol's lock
Each hop is given: "reveal zᵢ₊₁ to unlock; I will give you zᵢ back".
When Dave reveals r:
Carol learns r → computes z₁ = r + α₁ → unlocks from Bob
Bob learns z₁ → computes z₀ = r → unlocks from Alice
Animated: AMHL Unlock Propagation
Watch how the secret propagates backward with each hop learning a different value.
🔒
Alice → Bob
Lock: z₀ = r
waiting…
🔒
Bob → Carol
Lock: z₁ = r + α₁
waiting…
🔒
Carol → Dave
Lock: z₂ = r + α₁ + α₂
waiting…
Key Insight
The locks z₀, z₁, z₂ are computationally unlinkable to an outside observer (each looks random). Yet they are algebraically linked: learning r lets each hop derive their predecessor's claim key. This is the foundation of PTLC.
PTLC — Point Time-Locked Contracts
PTLCs replace hash-based locks with elliptic curve point locks, enabling cryptographic unlinking across hops and scriptless on-chain footprints.
// Alice's base secret point
S = s·G
// Each hop gets a blinded point:
T₁ = S + t₁·G // Alice → Bob
T₂ = S + t₂·G // Bob → Carol
T₃ = S + t₃·G // Carol → Dave
// t₁, t₂, t₃ are blinding scalars
// sent encrypted to each forwarder
Privacy Gains
T₁ ≠ T₂ ≠ T₃ — unlinkable!
On-chain: looks like key-path spend
No script = no payment type leak
Adaptor sig binds secret to sig
Side-by-Side: What Nodes See
Property
HTLC
PTLC
Hop lock value
Same H everywhere
Different Tᵢ per hop
On-chain reveal
Preimage R in Script
Adaptor sig completes → s extracted off-chain
Script complexity
Complex P2WSH script
Simple key-path Taproot
Payment correlation
❌ Possible
✅ Prevented
Wormhole attack
❌ Vulnerable
✅ Prevented
Lightning status
✅ Deployed (BOLT#3)
⏳ Spec drafted, not deployed
Why Schnorr?
Schnorr signatures are linear: σ₁ + σ₂ = σ₁₊₂. This linearity allows adaptor signatures to work — you can "complete" a partial signature by adding a secret scalar. ECDSA (used in legacy Bitcoin) does not have this property. Taproot (BIP341) enabled Schnorr on Bitcoin, making PTLCs feasible.
Adaptor Signatures — Step by Step
An adaptor signature is a "pre-signature" that becomes valid only when a secret scalar is revealed — binding cryptographic secrets to digital signatures.
Setup
Alice wants to pay Bob conditional on Bob knowing scalar s, where S = s·G is the corresponding public point.
S = s · G (Bob's secret s, public lock point S)
Alice knows S but not s. She uses an adaptor signature so that when Bob reveals s, Alice can extract it from the signature.
1️⃣
Bob creates adaptor signature σ̃
σ̃ = (k − s) mod n // k = nonce, using Bob's private key
This is not a valid Schnorr signature yet — it's missing the secret s. Bob sends (σ̃, S) to Alice.
2️⃣
Alice verifies the adaptor
σ̃ · G =? R − S (where R = k·G)
Alice can check that σ̃ is correctly formed w.r.t. S without knowing s. The verification passes, so she is confident: if Bob reveals s, he gets paid.
3️⃣
Bob reveals s → completes the signature
σ = σ̃ + s (now a valid Schnorr signature!)
Bob publishes σ on-chain to claim his payment. The signature σ is now valid and accepted by Bitcoin nodes.
4️⃣
Alice extracts s from the published signature
s = σ − σ̃ (Alice learns the secret!)
Alice observes σ on-chain, computes s = σ − σ̃. She now has the secret — no extra communication needed. She can use this to unlock her upstream PTLC.
Key Insight
Adaptor signatures bind a cryptographic secret to a digital signature.
You learn the secret when the signature is revealed — not before, not separately. This eliminates the need for separate hash revelation in scripts. Everything happens through the signature itself.
Security Properties
Pre-signature hidingσ̃ reveals nothing about s
ExtractabilityGiven σ, σ̃ → s is computable
UnforgeabilityCan't forge valid σ without s
Atomicitys is revealed iff payment succeeds
Scriptless Scripts — Hiding Logic in Signatures
Encode payment conditions inside the signature itself — on-chain, the transaction looks like a simple key-path Taproot spend with no script at all.
Traditional: Script-Based
Payment conditions are encoded in Bitcoin Script opcodes. The script is revealed on-chain when the UTXO is spent.
The same payment condition is encoded as a relationship between adaptor signatures and a secret point. On-chain: just a Schnorr signature.
// PTLC on-chain spend (Taproot):
// witness: <sig>
// scriptPubKey: <internal_key> OP_CHECKSIG
// The sig encodes the payment secret!
// Looks identical to any other Taproot spend.
No script revealed on-chain
Minimal witness size → lower fees
Indistinguishable from regular payment
Secret learned off-chain from sig delta
What "Scriptless" Enables
Atomic Swaps
Cross-chain swaps using PTLC adaptor sigs — no hash preimage needed. ETH↔BTC swap with a single curve point.
Discreet Log Contracts
Oracle-based contracts where the oracle's signature acts as an adaptor secret. No script, just signatures.
Lightning PTLCs
Replace HTLC in Lightning channels. Each hop has an unlinkable adaptor secret. Full route privacy.
Full Comparison Table
Property
HTLC
PTLC (Scriptless)
On-chain tx size
Large (P2WSH script path)
Small (Taproot key path)
Privacy on-chain
❌ Hash visible in script
✅ Just a Schnorr signature
Payment correlation
❌ Same H all hops
✅ Different Tᵢ per hop
Requires
SegWit (BIP141)
Taproot + Schnorr (BIP340/341)
Lightning status
✅ Active (BOLT#3)
⏳ Spec drafted, not deployed
Wormhole attack
❌ Vulnerable
✅ Prevented
Relative fee
Higher (more witness bytes)
Lower (key-path spend)
Andrew Poelstra's Insight (2017)
"Scriptless Scripts" was coined by Andrew Poelstra at the 2017 Milan Lightning Hack Day. The key observation: Schnorr's linearity allows payment conditions to be encoded into the signing nonce rather than in Script opcodes — making complex contracts look like simple signatures.
Wormhole Attack — and Why PTLC Fixes It
The wormhole attack lets a node that appears twice on a route skip an intermediate node, stealing fees while the skipped node gets nothing.
HTLC: Wormhole Attack
Route: Alice → Bob → Carol → Bob₂ → Dave. Bob₂ is a second node controlled by Bob. With HTLC, since all hops see the same H, Bob and Bob₂ can collude.
Alice→Bob→Carol✂ cut out→Bob₂→Dave
Attack steps:
1. Bob sees HTLC from Alice: lock = H, forwards to Carol
2. Carol creates HTLC to Bob₂: lock = H (same!)
3. Bob₂ connects to Bob off-chain: "I see H from Carol, you see H from Alice — let's skip Carol!"
4. Bob creates a shortcut HTLC: Bob → Bob₂ directly with fee profit
5. Dave reveals R → Bob₂ claims from Bob directly → Carol gets nothing
6. Alice still pays Bob's full fee; Carol is cut out
PTLC: Wormhole Impossible
With PTLC, each hop has a different adaptor secret point Tᵢ. Bob and Bob₂ cannot match their hops because they see different points.
Alice→BobT₁ = S + t₁·G
Bob→CarolT₂ = S + t₂·G
Carol→Bob₂T₃ = S + t₃·G
Bob₂→DaveT₄ = S + t₄·G
Why the attack fails:
Bob sees adaptor point T₁ from Alice. Bob₂ sees adaptor point T₃ from Carol.
T₁ ≠ T₃ — Bob and Bob₂ cannot match them without knowing the blinding scalars t₁, t₃.
The blinding scalars are chosen by Alice and encrypted per-hop via onion routing.
No shortcut is possible — Carol cannot be bypassed.
Formal Result
The wormhole attack requires the ability to correlate two hops on the same route. With PTLC, the correlation between Tᵢ and Tⱼ requires knowledge of the blinding factors, which are only known to Alice. Even if Bob controls multiple nodes, he cannot link them cryptographically.
Commitment Transactions — Revocation & Penalties
Channel state is tracked through commitment transactions. Old states are revoked by exchanging private keys — broadcasting a revoked state results in total loss of funds.
How Commitment Txs Work
Channel statePair of commitment txs (Alice's + Bob's)
Each state updateExchange new state, revoke old state
RevocationGive counterparty revocation privkey
Cheating penaltyCounterparty takes ALL funds
TimelockCSV: gives honest party time to react
Game-Theoretic Incentive
If Alice broadcasts state N-1 (old) instead of state N (latest), Bob uses Alice's revocation key for state N-1 to immediately claim all channel funds. The expected loss from cheating exceeds any possible gain.
Transaction Flow
OLD Commitment (State N-1)
Outputs:
• Alice's balance (with CSV timelock)
• Bob's balance (immediate)
• Any active HTLCs
⚠️ Broadcasting this = CHEATING
↓ Alice broadcasts old state ↓
Justice Transaction
Bob detects old state on-chain.
Uses revocation key for state N-1.
Spends Alice's CSV output immediately.
Claims 100% of channel funds.
✅ Honest party wins everything
Animated: State Update Protocol
Watch how two channel parties exchange revocation keys to safely advance state.
Alice
→ new state →
← revocation ←
→ revocation →
Bob
Eltoo: The Alternative (No Penalties)
Eltoo (SIGHASH_ANYPREVOUT, BIP118) proposes replacing the revocation mechanism with update transactions that can override any previous state. No penalty needed — any later state can always spend any earlier state. Simpler channel factory construction, but requires new sighash type.
⏳ BIP118 pending activationPart of LN-Symmetry proposal