Navigating Lightning Innovation: lipa’s journey from LDK to Breez SDK

lipa
10 min readMay 3, 2024

By Daniel Granhão

Here at lipa, we have decided to do something that we should have started a long time ago: openly share what we’re working on. To compensate for our previous silence, we’ll start with a post detailing our journey in building a mobile Lightning wallet.

Before we dig in, we should answer the biggest question: “Why another Lightning wallet? Aren’t there enough?”; Our answer lies not just in creating another app but in transforming the payment experience itself.

At lipa, our initial venture began with a straightforward objective: accelerating Bitcoin’s utility in everyday transactions. We developed a Lightning Point of Sale (PoS) application tailored for small businesses, designed to be simple to implement and use. However, it quickly became apparent that the real challenge wasn’t just in equipping businesses but also in encouraging consumers to embrace Lightning payments over traditional fiat currencies. This gap in the market steered us towards focusing on end-users, motivating them to discover the benefits of using Lightning for their transactions: speed, efficiency, and actual ownership.

Our observations also highlighted a crucial aspect: the user experience (UX) with existing wallets left much room for improvement. Many users found the concepts of liquidity and channel management complex and daunting, not to mention that in our opinion the aesthetics of most wallet apps could use some improvement. These insights shaped the foundation of our development philosophy for lipa wallet.

We started working on lipa wallet two years ago. Since then, a lot has happened. This post focuses on the biggest challenges and decisions we made regarding the Lightning implementation of the wallet.

Building on LDK

Ok, so we wanted to innovate primarily on the UX and UI levels, but we still needed an actual Lightning node implementation. What could we use? To answer this question, we had to decide on our initial requirements, and we settled on the following ones:

  • Non-custodial wallet — this was non-negotiable. We stand by the principles of Bitcoin, and we want our users to truly own their money
  • Send and receive using BOLT11 Lightning payments
  • Easy onboarding with zero-confirmation Just-In-Time (JIT) channels that are immediately usable after the initial payment is received
  • Seamless synchronization with the Bitcoin and Lightning networks, reducing any potential synchronization times to the bare minimum
  • Recovery requires nothing more than a recovery phrase (aka mnemonic or seed phrase), thus eliminating the need for users to manage their Lightning state actively
  • Compatibility with iOS and Android native applications to seamlessly use native smartphone features such as NFC and background notifications

Given these requirements, we considered several options, which can be broadly categorized as client-heavy or cloud-heavy solutions. In the past, we’ve called these categories “node on the phone” and “node on the cloud” but have since moved away from that terminology because a Lightning node isn’t a monolith but rather a collection of distinct components that can be separated between a client device and the cloud.

Client-heavy solutions

In this type of solution, most of a node’s components run directly on a user’s mobile device. The first and most naïve client-heavy solution would be to develop our own mobile-oriented Lightning node. This isn’t really feasible for two reasons, the first being that it’s a huge task by itself. The second is that the Lightning protocol is changing and developing further, so it’s very hard to keep up unless it’s your one and only focus.

A more feasible solution would be to package an existing node implementation such as the Lightning Network Daemon (LND). This approach was commonplace in the wallets available two years ago. The fact that these implementations were not made for mobile but for desktops and servers means that they can be inefficient in their use of resources, slow to start and take quite some time to sync to the network if the app isn’t opened for a while.

The last solution of this kind was to use the Lightning Development Kit (LDK). LDK is a set of Rust libraries that together provide the basic components needed to build a Lightning node. They minimize assumptions on the executing environment, meaning they can be used efficiently on mobile devices. Despite that, the high flexibility means that integrating it is quite more challenging than using a ready-to-go Lightning implementation, even if it’s orders of magnitude simpler than implementing a node from scratch.

Cloud-heavy solutions

On the other side, we have cloud-heavy solutions. These will attempt to offload as much as possible from the client, which is excellent for mobile devices, and run the offloaded tasks on the cloud. What’s crucial is that the user’s private keys never leave the user’s device. Because of this, the node’s signer component is one that must always remain on the client. During our research, we looked very closely at the Validating Lightning Signer (VLS) project, which promised to separate a signer from the rest of a Lightning node while keeping the signer smart enough only to sign approved transactions. We concluded that it wasn’t ready yet. We also considered Greenlight by Blockstream, which internally uses VLS to run Core Lightning (CLN) nodes on the cloud, but it was also not ready yet.

Choosing LDK

In the end, we decided to build our wallet using LDK. Cloud-heavy solutions showed promise but weren’t ready. Using LDK was a bet on using a more efficient solution and trying to do something different from what everyone else was doing at the time.

So, what did we achieve? After about a year of research and development, we had iOS and Android wallet apps running LDK, but not without some caveats. Let’s first dig into what we had to develop to get to that point.

What we built

We developed our own Rust library (lipa-lightning-lib) to encapsulate all the logic necessary to integrate LDK. LDK provides its own bindings to Swift and Kotlin, but we went with our own library and bindings for two reasons. The first is we noticed the bindings didn’t get the same love as the native LDK library. We don’t mean to downplay LDK’s team dedication. Still, it’s a huge task given the complexity of LDK’s interface, resulting in the bindings having less documentation and taking longer to get updated compared to the native library. The second reason is that integrating LDK is not trivial. Using our own Rust library allowed us to reduce code duplication between the iOS and Android apps significantly.

We deployed our own Lightning Service Provider (LSP) using LND and Breez’s lspd. This allowed us to get JIT channels for easy onboarding. It wasn’t necessarily our goal to operate an LSP, but we wanted to learn what running one entails.

To avoid the more significant sync times required to build a local view of the network graph, we were one of the first adopters of the Rapid Gossip Sync (RGS) project. Put simply, it allows light clients to fetch compacted updates of the Lightning network graph, thus eliminating the need to use gossip to build one themselves.

The last thing worth mentioning is our implementation of safe channel state persistence. We developed our own simple solution for persisting encrypted backups of channel states, meaning that users could recover the wallet on a new device just by using their recovery phrase.

Our main struggles

Despite having reached the point where we had all of these systems set up, we never got the node implementation to work reliably.

The main issue we faced was very unreliable pathfinding/routing. Receiving payments worked pretty well, but our use of LDK’s pathfinding implementation resulted in very disappointing outgoing payment success rates. Only extremely small payments worked, with any payments with useful amounts failing consistently. We spent a lot of time on this problem, and we pinned it down to multiple issues:

  • Buggy rapid gossip sync implementation
  • Inherent limitations of rapid gossip sync: RGS works by creating snapshots of the network graph, meaning that updates can take longer to reach the client, in some cases making the client route through a channel that no longer is available or had its parameters changed (something that routing nodes apparently do pretty often)
  • LDK’s adoption was still in relative infancy, meaning that not many people used its pathfinding implementation, leading to its poor performance not having been addressed

We later found out that Mutiny, another wallet integrating LDK, was also experiencing the same issues, and they addressed them more deeply in this blog post.

The other big struggle, which is now easy to identify in retrospect, is that working on a wallet client, in addition to developing and running all of the required services (e.g., LSP, channel state persistence, etc.), and designing and implementing novel wallet UX, is too much for a single team to handle.

The Big Pivot

Given these struggles, we started trying to offload some of the responsibilities we had taken, beginning with the LSP. We contacted several potential partners that could provide the LSP for the lipa wallet. One of these potential partners was, of course, Breez. During our talks, Breez introduced us to Breez SDK, a cloud-heavy Lightning node solution that relied on Greenlight and, thus, on VLS, coupled with the infrastructure maintained by Breez.

Breez SDK

When compared with our LDK solution, Breez SDK presented several key advantages.

Improved payment reliability

Breez SDK has the potential to provide much more reliable outgoing payments since it relies on pathfinding being done by CLN on the cloud. This does mean some privacy is lost, but confidentiality can only be valuable if payments work in the first place.

Concurrent access

Another great advantage of Breez SDK is that it supports out-of-the-box concurrent access from multiple devices due to its architecture. We need to state that something similar was in the roadmap for LDK, namely using their Versioned Storage Service (VSS), but with Breez SDK, it’s ready now. Besides providing peace of mind in knowing you can’t mess up your wallet by switching devices, safe concurrent access also enables novel use cases that have yet to be explored, such as multi-account nodes.

Comprehensive solution

The SDK is a very different project than LDK. It doesn’t try to offer a great deal of flexibility, but it provides a very comprehensive solution for our requirements. Namely, with it, we automatically get access to an LSP run by an experienced team. On-chain to Lightning swaps and reverse swaps. Paying and withdrawing using LNURL. Zero channel reserves. The possibility of sending out all funds. The list goes on, and all of this is accessible through a relatively simple interface.

Promising roadmap

The roadmap is at least as interesting as the current feature set. Access to other LSPs using the new LSP specification is in the works, thus working towards the vision of an LSP market. Splicing will enable on-chain cost savings, and in the future, it might even ship with a local LDK node backend instead of using Greenlight.

Responsibility separation

Last but not least, we see it as a significant advantage that we get to focus on what we initially set out to innovate, which is the Lightning wallet itself, and know that the experienced teams at Breez and Blockstream deal with low-level Lightning challenges. Each team leans on their expertise, and all benefit from the work of others. Instead of having a model where wallet developers directly use Lightning implementations such as LND, LDK or CLN, or even worse, try to implement the BOLTs themselves, we see as a much better alternative the model depicted in Figure 2. In this model, wallets are developed on top of tools like Breez SDK accompanied by Lightning services. We end up with a layered approach, where each layer builds on top of the other, allowing specialization in different areas.

What about Drawbacks?

Well, it turns out, as it always does, that these advantages don’t come without drawbacks.

An important one is that Greenlight is a centralized solution. Even though Blockstream doesn’t have control over user funds, it is still a single point of failure for all the wallets built using Breez SDK. Either by mistake or maliciously, Blockstream can prevent users from moving their money, at least temporarily. The current solution for this is for users to run a local instance of CLN so that they can recover their funds using static channel backups. Also, regarding its centralization, Greenlight could be a target for attackers who seek to disrupt Blockstream or its users. Any downtimes, either resulting from an attack (e.g. DoS) or from maintenance operations, directly affect the ability of all Breez SDK wallets to be used.

Another disadvantage, one that has already been briefly mentioned, is that pathfinding is done externally, meaning that all payment recipients are shared with Blockstream. In contrast, with local pathfinding, as with our LDK solution, users enjoy better privacy over whom they pay.

Outcome

We concluded that the benefits greatly outweigh the drawbacks, especially considering some of the drawbacks could even be short-lived. For example, there are plans to implement local pathfinding to improve payment privacy. Also, the plans to potentially integrate LDK as a backend to Breez SDK can address any worries about Greenlight’s centralization.

The bottom line is that it’s a massive effort to build a feature-rich wallet using lower-level tools like LDK while developing and running the required services independently. It’s a much better plan to separate concerns, with Breez focusing on the LSP and lower-level Lightning development, letting us focus on great UX and user-centric features.

Where We Are at and the Path Forward

We’ve come a long way since we started integrating Breez SDK. When we started making the switch, we identified several areas needing improvement within the SDK, such as the error model and documentation. We’re happy to say that the vast majority of the issues we encountered were quickly resolved by the Breez team, who were always very accommodating to our requests. Other times, we’ve brought in our expertise and real-world perspective by contributing to and enhancing the SDK directly. Overall, we can say collaboration has been great.

Not everything has been perfect, though. Our main issue with our current solution is performance, namely payment times, which can be slow (> 5 seconds) even to well-connected nodes. It has been improving, and we sleep well at night knowing the Greenlight team at Blockstream is tirelessly working on improving it even further.

This wraps up the review of our journey. We could go into much more detail and are open to doing so, so feel free to reach out to us if you have any questions you would like to get answered. You can find us on X/Twitter, Telegram, or our website.

--

--