Hey there, Lightninger! Last week was a busy one, with a big discussion around a new Lightning vulnerability, self-custodial Lightning Addresses, and more! Let's unwrap it all, shall we?
EDIT: a previous version of this post mistakenly stated that mitigations against replacement cycling attacks were in place in LND by version 0.17.0-beta, while all mitigations were actually deployed in LND by version 0.16.1-beta (i.e. ~5 months earlier).
Taproot Assets Mainnet Release
The Taproot Assets protocol is now officially available on mainnet, with the release of the version 0.3.0 of the Taproot Assets daemon. This is still alpha software, but this also means that the protocol is now forward compatible: no more breaking changes!
While this first mainnet version only brings on-chain capabilities, Lightning will come soon™, now that two prerequisites (namely unannounced Taproot channels and the draft specification of the Taproot Assets channels) have been delivered.
Mash x TFTC
Mash and TFTC unveiled the new Mash experience, as well as their partnership aimed at building the future of independent content publishers on the internet. You can give it a try here, but the new reactions and easy tipping interface really feel fresh and polished.
Reactions are a very fun and smart interaction between an audience and a publisher: users can add some free reactions to paragraphs, and they can also spend a few sats sats to unlock paid reactions. Interestingly, while you need a Mash account to publish a free reaction, paid reactions can be posted anonymously, since the payment itself makes it not spam. Eager to see what comes next: Nostr integration, end-to-end self-custody? 👀
Wallets & Tools
Relai, BitBox Integrate Lightning Using Breez SDK
Last week was a big one for the Breez SDK. As a reminder, the SDK is a "Lightning as a Service" product that enables app developers to bring Lightning to their user, without anyone else than the user taking custody of the funds at any time. This is achieved thanks to Blockstream's Greenlight, which the Breez SDK uses behind the scene and basically consists in a Lightning node running in the cloud while the private keys remain on the end-user's device.
At the Lugano Plan B forum, European broker Relai unveiled their integration of a self-custodial Lightning wallet into their app, using the Breez SDK. Right now, Relai users can already enjoy a self-custodial on-chain experience, as any bitcoins they buy through the app are sent straight to their own wallet that lives inside the Relai app, and for which only the user knows the private key. It only makes sense to add a self-custodial Lightning wallet to the mix, which Relai users will be able to use for daily spending once this new feature is opened to the world (as it currently is in a closed beta).
But that's not it! Hardware wallet (sorry, signing devices) manufacturer BitBox also announced that they are currently working on a MVP for a Lightning wallet inside the BitBox app, again leveraging the Breez SDK. Much like Ledger Live or the Trezor Suite, the BitBox App is a software companion you can use in conjunction of your BitBox hardware device to receive and send transactions, view your balance, or even buy Bitcoin through an integration of Pocket or MoonPay. BitBox would be the first signing device manufacturer to add Lightning capabilities to their app, which brings a number of interesting features such as unified backup or streamlined funds management between on-chain and Lightning (especially once Breez integrates splicing). Nice!
Zeus Update Brings Lightning Addresses
Late last week Zeus released the open beta of their new wallet with an embedded Lightning node inside. This new wallet was already available to alpha-testers, but is now available for everyone to try.
With this release comes a very interesting new feature: self-custodial Lightning Addresses. Indeed, using Zaplocker (which we already extensively covered here) in the Olympus LSP, it is now possible to receive sats asynchronously to a static identifier, with very little third party risk compared to other solutions. In other words, it's probably the best ease-of-use to self-custody ratio out there, and while one could argue that the Zaplocker setup isn't completely trustless, I think it's still trustless-enough for a lot of Lightning Address use cases and, in any case, far superior to any custodial solution. The tradeoffs relative to Zaplocker are extensively discussed on the project's repository, but mainly resolve to:
- the use of 24-hours-long hodl contracts to handle the asynchronicity of the payment, which doesn't scale very well because the whole route between the sender and the receiver needs to hold the payment in-flight until the receiver comes back online or it expires. This was highlighted as being a major setback by respected Lightning developers such as Matt or Roy. The Zeus account mostly eluded this criticism, pointing out that this issue would be addressed by having LSP-to-LSP asynchronous payments, where the payment is held only in the channel between the sender and its LSP until the receiver comes back online and the payment is finalized. Stretching things a bit, this new feature almost resembles a "Trojan Horse" aimed at incentivizing rapid development of this new piece of the protocol.
- all the issues with the Lightning Address server (e.g. the LSP in our case) being able to steal funds from the receiver are addressed in Zaplocker by using a Nostr-based attestation system, which essentially helps keep the LSP in check by enabling the sender and the receiver to check that the correct amount of funds is indeed sent to the receiver. While this attestation mechanism isn't more widely spread (and, to my knowledge, only Zeus implements it as of today) users should keep in mind that the setup still involves trust. Only when both sender and receiver perform attestation checks can a payment using Zaplocker be considered trustless.
Still, that's a very impressive development. Shoutout to Supertestnet and the Zeus team for achieving "self-custodial" "trustless" Lightning Addresses, and let's see how it goes!
Other Wallet Updates
Bitkit released a bunch of bug fixes, enhancements and new features, notably with the just-in-time channel creation when trying to receive without enough inbound liquidity, and the ability to send to Taproot on-chain addresses.
Nayuta, a mobile Lightning wallet with an embedded LND node and a dedicated Lightning Service Provider, became open source.
Spec & Implems
Is Lightning Really Broken?
You've probably seen it: last week Antoine Riard publicly disclosed a vulnerability in the Lightning Network that could allow an attacker to steal funds from a routing node. That sounds scary but before we continue note that:
- all major Lightning implementations (Core Lightning, Eclair, LDK and LND) have deployed mitigations against this attack (respectively at versions 23.08.01, 0.9.0, 0.0.118 and v0.16.1-beta) ;
- the attack is actually quite complex to perform, and can easily fail unless the attacker is very sophisticated.
There are already some awesome summaries of this "replacement cycling" attack, but I'll try to give a short summary of how it works. When routing a transaction, a routing node has an outgoing HTLC which goes to the next node in the route, and an incoming HTLC which comes from the previous node in the route. An HTLC is essentially a conditional payment, which can be claimed by its recipient by revealing the appropriate secret (called the preimage) or expires after a certain block height. At any time, the receiver of an HTLC can claim it by revealing the preimage but, once the expiration block height is reached, the sender can claim the funds back. However, even after the expiration, if the sender didn't reclaim the funds the receiver can still take them using the preimage.
A routing node hence needs to avoid finding themselves in a situation where their outgoing HTLC can still be claimed by the receiver while the incoming HTLC has timed-out (i.e. reached its expiration block height) and was reclaimed by the previous node in the route. To prevent this scenario from happening, the expiration height of HTLCs are carefully set in an increasing order from the end of the route to the beginning. This way, the outgoing HTLC always expires a few dozens of blocks before the incoming HTLC, leaving some time for the routing node to reclaim the funds.
Usually, when everything goes right, HTLC expiration is handled off-chain between cooperative peers. However, if the next node on the route becomes unresponsive, the routing node has no choice but to resolve the outgoing HTLC on-chain in a timely manner, i.e. before the incoming HTLC expires in turn. The routing node does so by publishing the latest commitment transaction on-chain, which represents the latest state if the channel, with the outgoing HTLC represented as an output of the transaction ; and then spending the outgoing HTLC output in what is called the HTLC-timeout transaction once the commitment transaction is confirmed.
However, if the next node in the route knows the preimage (for example because they're the payment's recipient), they can publish their own transaction spending the HTLC from the commitment transaction in a transaction called the HTLC-preimage transaction, in which they reveal the preimage to claim the funds on-chain. Since the receiver spends the same output as the routing node, their transaction needs to pay a higher fee in order to be accepted by other nodes. It then replaces the routing node's HTLC-timeout transaction, but the routing node learns the preimage in the process and is hence able to claim the incoming HTLC. Nobody is aggrieved in the process, a channel was closed but the payment was fulfilled on-chain and everyone got what there were due.
However in the attack scenario, the two nodes around the routing node collude to steal the funds of the HTLC. To do so, they start by sending an unrelated transaction ahead of time, so that this transaction is still in the mempool when the routing node publishes the HTLC-timeout transaction on-chain. Then, once the routing node published the HTLC-timeout transaction, they publish the HTLC-preimage transaction, but with 2 twists:
- the transaction spends both from the HTLC's output in the commitment transaction and from the unrelated transaction they placed in the mempool earlier ;
- they manage to have the HTLC-preimage transaction reach miners without the routing node learning about it. In other words, the HTLC-preimage transaction propagates quickly in the network but never enters the routing node's mempool, and/or is sent directly to miners.
As seen before, when they broadcast the HTLC-preimage transaction, it replaces the routing node's HTLC-timeout transaction in the mempools it reaches. But then, they also replace/cancel the unrelated transaction they broadcast earlier with another one. Since this transaction no longer exists, and the HTLC-preimage transaction spends from it, the HTLC-preimage transaction itself is cancelled. The output corresponding to the HTLC is unclaimed, but at the moment in most nodes mempool there is no HTLC-timeout nor HTLC-preimage transaction claiming it.
Unaware of the situation, the routing node doesn't know that their HTLC-timeout transaction has been insidiously removed from other nodes mempools. Detecting that the transaction doesn't confirm, they might suspect a propagation issue and rebroadcast the HTLC-timeout transaction. However, every time they do so, the attacker performs the replacement cycle again, first replacing the HTLC-timeout transaction with the HTLC-preimage transaction, before quickly cancelling it as well to before it it confirmed or reaches the routing node.
After a few blocks (typically around 34), the incoming HTLC expires too. The node that sent this HTLC to the routing node can therefore reclaim it on-chain by publishing this HTLC's timeout transaction. Once this transaction is confirmed, the receiver on the other side of the routing node publishes the HTLC-preimage transaction and, this time, doesn't replace it. The node before the routing node claimed the funds of the incoming HTLC using the HTLC-timeout transaction, and the node after the routing node claimed the funds of the outgoing HTLC using the HTLC-preimage transaction. In the middle, the routing node is left with a loss the size of the payment.
However, the attack isn't straight forward, and can quite easily be mitigated, at least to some extent:
- the attack relies on the victim never being aware of the HTLC-preimage transaction. It hence requires very close control of the victim's mempool (for example through an Eclipse Attack), which is quite hard to maintain, especially if the victim's node actively tries to combat such control ;
- the victim can largely mitigate the attack by aggressively rebroadcasting their HTLC-timeout transaction with high fees, forcing the attacker to perform the next replacement cycle with even high fees, thus rendering the attack uneconomical as the payment's amount starts to be dwarfed by the on-chain fees.
What was very interesting were all the discussions on assessing the attack's criticality and proposing mitigations, which Bitcoin Optech outstandingly covered here.
Removing Channel Reserve For Mobile Wallet Users
Bastien Teinturier proposed than channels between a user and their LSP might get rid of the channel reserve requirement on both sides, simplifying the user experience and enhancing the LSP's capital efficiency.
The channel reserve is a portion of a channel's funds (usually 1%) that each party must maintain on their side of the channel at all time to ensure that they always have something at stake. If a party tries to cheat by publishing a revoked state, the other party can hence punish them by taking at least the channel reserve amount. Without this requirement, a node could be incentivized to cheat when their balance on the channel is null, since they don't have any funds at risk.
In the case of a channel between a LSP and user, such protection seems less necessary. Indeed, the reserve requirement on the LSP side could be removed, since the LSP pays the channel closing fees and hence always at least this on-chain transaction fees at stake, as well as their reputation. The reserve requirement on the user's side can be removed too if the LSP values a nice user experience (and the routing revenue that accrue from it) over the (limited) risk of a user trying to cheat once their balance reaches zero and they have nothing at stake anymore.
Most comments agreed that removing the reserve requirement in this specific setup made sense, with some also highlighting that it could make sense in plenty of other situations where a trust relationship exists between the two peers. It would then be possible to agree on a 0-reserve policy, just as is currently the case with other protocol extensions and optional features.
Batch Exchange Withdrawal To Lightning Requires Covenants
Bastien Teinturier (yes, him again!) also shared his thoughts earlier last week on what batched Lightning withdrawals from an exchange would look like. When a user withdraws from an exchange, it can happen that the channel(s) they currently have (for example with their LSP) isn't big enough to accommodate the incoming payment. Right now, what would happen is each LSP would need to perform a splice-in transaction, which is not very efficient in terms of on-chain footprint. A better solution would be for the exchange to be able to batch all those splicing transactions into one single transaction. However, such a mechanism either requires all users (and their LSPs) to be online at the same time to perform this transaction interactively, or we'd need to have OP_ANYPREVOUT to enable this in a non-interactive setup while keeping user's funds safe at all time.
Indeed, without ANYPREVOUT, an issue arises in the non-interactive setup where the user's LSP can blackmail them, since after the splicing the user has now way to spend the funds from the "new channel". This is this specific hiccup that ANYPREVOUT addresses.
The discussion then delved into how one participant could disrupt the batched splicing by publishing an old state of their channel. As Teinturier pointed out, this is already the case in regular 2-players splicing transactions, which only means mitigations must be put in place for splicing in general against this kind of griefing attacks.
Trivia Of The Week
What is "probing" and how is it a potential privacy concern?
AnswerProbing refers to the act of sending small payments or test payments through the network to gather information about channel balances and routing failures. This can be a privacy concern as it may allow an attacker to infer the balances of channels or even identify the endpoints involved in a particular payment. It is, however, commonly carried out by some service providers to ensure a smooth user experience to their customers.
Dans le dos la croix
Devant le rutilant pavage
Chargé d'embruns et de poussières
De la mer.
Dallage commun des cathédrales du monde entier
De Vierge en procession
A perte d'horizon.
Interestingly, Pocket recently launched a Lightning top-up service, which lets users buy Bitcoin straight to their Lightning wallet. That might be another nice feat of having a self-custodial Lightning wallet inside the BitBox app! ↩︎
Note that while the underlying seed is the same, the Lightning wallet is actually a completely different wallet that the on-chain one(s). Only the private key of the Lightning wallet is held on the app, and the seed itself remains safe in the microSD card and/or backup you usually use with your BitBox signer. ↩︎