Loop Out In-depth
by Joost Jager
In a previous blog
post we
announced Loop Out, a
non-custodial service to obtain inbound liquidity by offloading funds to a
regular on-chain address. This post goes deeper into some of the technical
details of Loop Out and assumes knowledge of Bitcoin, Lightning and hash locked
contracts.
Inbound Liquidity
The general way of obtaining inbound liquidity in the Lightning Network is to send out a payment through the channel you would like to receive on. This in itself is a unique aspect of Lightning and not like any other payment system. A way to look at a channel is as a closed tube between you and your channel peer that contains marbles. Marbles represent money. By sending a payment (for example to buy goods from a supplier), marbles move to your peer’s side of the tube. A subsequent payment to you (your customer buying the goods) moves the marbles back to your side.
In an ideal future where everyone uses Lightning, this system is self-balanced. You pay to others as much as you receive from them and the marbles just keep rolling back and forth. However, with the state of Lightning today, this is not necessarily true. A merchant for example who sells products via Lightning but pays its suppliers via another payment method will likely accumulate more and more balance in its Lightning channels until customers can no longer make payments. All the marbles have moved to the merchant’s side of the channels.
The solution that Loop Out offers in this situation is to make a payment (push the marbles away from you) and receive the money back at an on-chain address. You are effectively looping the payment back to yourself, hence the name Lightning Loop.
Any channel can be Looped out over and over again. There is no need to open new channels and the user can pick the peer they want to loop out through. This is an advantage over existing services that “sell” a channel between the service and the user. With those services, any incoming payment to the user will be subjected to the routing fees that the service charges. They are bound to the routing policy of their target service. In addition to that, many users opening channels with the same service creates a single point of failure.
We wanted to offer the Loop Out service in a way that minimizes the amount of trust required. It would definitely have been much easier to build a full custodial service, but we believe there is value for the user in not having that counterparty risk. And fortunately the bitcoin blockchain offers the tools to do so.
Mechanism
The basic mechanism that is used in Loop Out is also known as a submarine swap. There are multiple variations of the submarine swap, but the process we use consists of the following high level steps:
-
The user of the service generates a secret hash preimage.
-
The user sends a payment tied to this hash to the Lightning Loop server. The server is not yet able to settle this payment because it doesn’t know the preimage yet. Instead, it holds on to the payment until it discovers the preimage. This part is implemented using a hodl invoice.
-
The server publishes an on-chain transaction to an output that can be spent by disclosing the secret preimage (a hash locked output).
-
The user sweeps that output to his wallet. He can only do this by disclosing the preimage in the spending transaction.
-
The server picks up the preimage from the spending transaction and uses it to settle the Lightning payment that it is still holding on to. This last step completes the swap.
If the server holds onto the Lightning payment and never publishes the on-chain transaction, the payment will time out and the funds will be returned to the user. This forms the non-custodial characteristic of the service. There is a slight penalty though for the user in the time out case, because it will lock up their funds until the time out.
Prepayment
The Loop server needs to spend money on publishing an HTLC on-chain. In case the user doesn’t follow through with the swap, we would lose that money. Left unchecked, this could introduce a DoS vector tying up the spare UTXOs of the Loop System. To prevent this the server requires a second Lightning payment called the prepayment to be made along with the swap payment. The idea is that if the swap doesn’t succeed, but the server did publish the on-chain HTLC, the server keeps the prepayment as a compensation for the loss in miner fees.
Sweeping Back to the Wallet
The preimage is revealed when the user’s sweep transaction enters the mempool (step 4 above). From that point onwards, the preimage is to be considered public knowledge and the user should expect their Lightning payment to be settled at any time. It is for that reason that the user needs to make sure that the transaction confirms. One thing that can get in the way of this is publishing the sweep transaction with a too low miner fee. However, the user can leverage fee bumping tools such as RBF and CPFP to ensure a timely confirmation.
The risk of the sweep transaction not confirming is dealt with in Lightning Loop by publishing with RBF enabled and trying to replace the sweep transaction in every block with a new transaction that is based on the latest fee estimate. The transaction fee is capped by the maximum miner fee that the user specified when the swap was initiated to avoid overpaying on-chain.
Time Pressure
Unfortunately for the user, there is not an endless amount of time to get that sweep transaction confirmed. The server does not publish the hash locked output without having a way to reclaim the funds in case the user doesn’t follow through. There is a second spending path that requires a certain block height to be reached. At that expiry height the server can reclaim the funds. So the on-chain swap output is locked by both a hash and a time, which makes it a hash-time locked contract (HTLC) similar to HTLCs used in regular Lightning payments.
The actual expiry height of the HTLC is picked by the server when the swap is initiated and checked against an acceptable minimum by the Loop client implementation. If the server proposes an expiry height that is too soon, the off-chain swap payment will not be paid and the swap will be aborted. The reason for this is that the user needs to have a reasonable opportunity to get the sweep transaction confirmed.
When the user discloses the preimage by inserting the sweep transaction in the mempool, the timer starts ticking. The users needs to get that transaction confirmed before the expiry height is reached and server reclaim path opens up.
This timer ticking complicates the situation around getting the sweep transaction confirmed. Possibly the user needs to become more aggressive with fee bumping when getting closer to the expiry height. He might even want to exceed the maximum set miner fee then, because the alternative may be to lose the full swap amount.
Fair Server
For the Loop client, our goal has been to implement it in a way that makes no assumptions about the server behaviour. It should be prepared for an adversary server that does all it can to steal from the user. An example of this is that all values received from the server and the parameters of the on-chain HTLC are checked locally by the client. If anything exceeds acceptable ranges, the swap is aborted.
Despite those preparations, we did implement a ‘fair’ server. Wherever we could choose between different behaviours, we chose the behaviour that is most beneficial for the user. This differs from an adversary server that would seize any opportunity to maximize profit. Because the swaps are non-custodial, there are fortunately not many of those opportunities. They mostly arise around non-happy flows and mistakes on the part of the user.
An illustration of this is cancellation of swap payments. When the server needs to reclaim funds after the HTLC expiry and the timeout transaction is confirmed sufficiently, it cancels back the held off-chain payment immediately. It could hold on to it longer, hoping that for some reason the preimage would still pop up in the mempool, but doesn’t do so.
Channel Tricks with Loop
Recursive Loop Out
With Loop Out it is possible to use an amount of money X to obtain far greater inbound liquidity than X. The idea here is to open a channel and loop all funds out. With the received on-chain funds another channel can be opened and this channel can be Looped out too. This process can be continued as long as there are funds left, because of course with every step fees need to be paid to miners, routing nodes and the Loop service. Some engineers on our team have taken to calling this method a “LoopDeLoop”.
The end result of this is that routing nodes will have a lot of funds committed to the user’s node. And hopefully for them, they will earn some routing fees in exchange for that commitment.
This also underlines the need for routing nodes to monitor their channels and close the ones that don’t yield any returns.
Loop Out to External Address
The default Loop Out behaviour is to Loop out to a wallet address of the user. But it is also possible to specify an external address. It allows an on-chain payment to be made from a node that has all of its funds committed in channels.
Similarly a channel can be Looped out directly to an exchange. This can for example be valuable for merchants that need to pay out suppliers or employees via fiat.
Conclusion
The added DoS defense of the pre-payment allows the service to operate in a manner where we’re compensated for any failed on-chain swaps. The pre-payment amount is nominal and is at most a few thousands satoshis. Within the API and on the CLI, the user executing the swap is shown the prepayment amount up front where they can agree to it.
Aside from that, standard timeout handling with respect to incoming and outgoing CLTV outputs with HTLCs are required. This is no different from the level of time lock management required in Lightning. However, with good fee selection heuristics the impact of this can be brought down to a minimum.
We think the points above are marginal compared to the unique advantages that Loop Out offers. It gives users the flexibility to obtain inbound liquidity from anyone. It does not push towards a centralized network topology and provides a way to re-use existing channels thereby extending their lifetime.
The next step for us is the release of Loop In. With Loop In, any channel can be topped up with an on-chain payment. There is no more need to close an otherwise good channel and reopen a new one.
Stay tuned.