Why Nostr?What is Njump?
fiatjaf /
npub180c…h6w6
2024-01-14 14:55:28

Lightning channels without HTLCs

Lightning channels without HTLCs

` DISCLAIMER: the following design is flawed and kept only for the history.

What follows is a way to design Lightning channels that don’t use standalone HTLCs at all: instead the hashlocks are the Settlement transactions themselves, so instead of a Settlement have as many outputs as pending HTLCs, instead they always have 2 outputs: the 2 peers in the channel, but there are as many pairs of Update/Settlement transactions as there are combinations of payments in-flight.

It assumes Eltoo exists and there are Update transactions that can attach to any previous Update transaction or to the funding transaction, and Settlement transactions, spendable after a CSV, one for each Update transaction.

Instead of explaining more, I’ll give some examples.

Examples

In the following super-simplified notation I’ll just treat the combination of Update+Settlement as one “state”, otherwise this would be a mess.

Consider that Alice and Bob have a channel with a total of 100 BTC. When at rest, their channel state reads as follows:

s1[Alice 50 | Bob 50]

This is the initial state of the channel. `s1` here means the sequence of this Update/Settlement according to odd SIGHASH_NOINPUT CLTV rules which are abstracted away. In the following cases there will be `s2`, `s3` and so on.

Simple case of routed payment

Alice tries to route a payment of 7 BTC through this channel using the hash h1 = H(p1). The previous state is discarded and replaced with the following:

  • current design:

    s2[Alice 43 | HTLC(if p1 then Bob else Alice after some time) 7 | Bob 50] – where p1 is the preimage

    if the payment is fulfilled, Alice gets p1, a new state is created as s3[Alice 43 | Bob 57]; if it’s canceled the state is replaced with s3[Alice 50 | Bob 50]; if Bob disappears and the timeout expires Alice closes the channel as s2[43|7 to Alice|50]; if Bob has p1, Alice disappears and the timeout expires Bob closes the channel as s2[43|7 to Bob|50].

  • proposed design

    Instead of a single state that includes an HTLC as an output, we have two states that both peers will keep:

    s2[Alice 50 | Bob 50] s3[if p1 then [Alice 43| Bob 57]]

    if the payment is fulfilled, Alice gets p1, a new state is created as s4[Alice 43 | Bob 57]; if it’s canceled the state is replaced with s4[Alice 50 | Bob 50]; if Bob disappears and the timeout expires Alice closes the channel as s2[50|50]; if Bob has p1, Alice disappears and the timeout expires Bob closes the channel as s3[43|57]; if Alice closes the channel without talking to Bob, but Bob has p1, he publishes s3 after her publishing s2; if Bob closes the channel without talking to Alice she sees p1 as broadcasted and is fine.

A case with 2 payments in-flight

What happens if besides the payment described above Alice then tries to route a new payment of 20 BTC using h2 = H(p2) while the previous is still pending.

  • current design: I’ll skip this as it’s basically the same thing but with a new HTLC and everybody knows it.

  • proposed design:

    Now the parties have to hold 4 transactions:

    s2[Alice 50 | Bob 50] s3[if p1 then [Alice 43 | Bob 57]] s3[if p2 then [Alice 30 | Bob 70]] s4[if p1 and p2 then [Alice 23 | Bob 77]]

    If one of the payments is fulfilled, or none, the scenarios are basically the same as in the previous case; If Alice gets p1 and publishes s3[43|57] without waiting for p2, then later p2 comes, Bob can publish s4[23|77] and don’t be harmed; Other scenarios should be similar to the previous case.

A case with one pending payment in each direction

Now imagine that while Alice is routing the first payment of 7 BTC another payment comes in the opposite direction, for 11 BTC using h3 = H(p3).

  • current design: I’ll skip this as it’s basically the same thing but with a new HTLC and everybody knows it.

  • proposed design:

    s2[Alice 50 | Bob 50] s3[if p1 then [Alice 43 | Bob 57]] s3[if p3 then [Alice 61 | Bob 39]] s4[if p1 and p3 then [Alice 54 | Bob 46]]

    If one of the payments is fulfilled, or none, the scenarios are basically the same as in the previous cases; If Bob gets p1 and publishes s3[43|57] without waiting for p3, then later p3 comes to Alice, she can publish s4[54|66] and don’t be harmed; Other scenarios should be similar to the previous case.

A case with 4 pending payments (you can stop reading now)

Consider now that there are two payments going to one direction and two in the other direction. And now we’re going to specify the delay for each payment. The delay means the time for which we’ll wait for each to be fulfilled or canceled.

h1: 7 BTC from Alice to Bob – delay: 10 blocks h2: 20 BTC from Alice to Bob – delay: 20 blocks h3: 11 BTC from Bob to Alice – delay: 30 blocks h4: 5 BTC from Bob To Alice – delay: 40 blocks

Now there are 16 transactions to be stored.
The CSV values are given on the Settlement transaction of each of these states, such that Settlements with higher sequence numbers have always time to be published.
Since the next payment expiring is the first, which will expire in 10 blocks, this entire "batch of states" have only that time to live. If it doesn't get updated with the fulfillment and/or cancellation of at least one of the pending payments and thus rewritten then the channel must be closed, probably with s2[50|50], to prevent loss of funds.

s2[Alice 50 | Bob 50] -- CSV 50
s3[if `p1` then [Alice 43 | Bob 57]] -- CSV: 40
s3[if `p2` then [Alice 30 | Bob 70]] -- CSV: 40
s3[if `p3` then [Alice 61 | Bob 39]] -- CSV: 40
s3[if `p4` then [Alice 65 | Bob 55]] -- CSV: 40
s4[if `p1` and `p2` then [Alice 23 | Bob 77]] -- CSV: 30
s4[if `p1` and `p3` then [Alice 54 | Bob 46]] -- CSV: 30
s4[if `p1` and `p4` then [Alice 48 | Bob 52]] -- CSV: 30
s4[if `p2` and `p3` then [Alice 41 | Bob 59]] -- CSV: 30
s4[if `p2` and `p4` then [Alice 35 | Bob 65]] -- CSV: 30
s4[if `p3` and `p4` then [Alice 66 | Bob 44]] -- CSV: 30
s5[if `p1` and `p2` and `p3` then [Alice 34 | Bob 66]] -- CSV: 20
s5[if `p1` and `p2` and `p4` then [Alice 28 | Bob 72]] -- CSV: 20
s5[if `p1` and `p3` and `p4` then [Alice 59 | Bob 41]] -- CSV: 20
s5[if `p2` and `p3` and `p4` then [Alice 46 | Bob 54]] -- CSV: 20
s6[if `p1` and `p2` and `p3` and `p4` then [Alice 39 | Bob 61]] -- CSV: 10

As in the previous cases, if at any time one peer tries to publish any of the Updates without waiting for remaining payments to be either canceled or fulfilled, the other peer can just wait for the missing preimage to arrive, gather the preimages they already know or which were broadcasted with the transaction and fulfill an Update that is higher in its sequence number.

Comments

Advantages:

  • Any payment, no matter how small, is taken into account in the balance, no more trustfulness
  • My understanding is quite poor, but I feel it’s simpler than the current channels and even simpler than Eltoo channels.

Drawbacks:

  • The number of needed stored transactions is 2^n for n payments in-flight.
  • Big scripts in Settlement transactions that check many hashes are terrible if they ever have to be published. Is it possible to turn them into a single adaptor-signature-magic scriptless script? I hope it is, but have no idea.
Author Public Key
npub180cvv07tjdrrgpa0j7j7tmnyl2yr6yr7l8j4s3evf6u64th6gkwsyjh6w6