A Look at LDK’s Dual-Funded Channels Implementation
We take a closer look at the progress and architecture of the dual-funded channel implementation in the Lightning Development Kit (LDK).

Written by Duncan Dean
Dual-funded channels—or V2-established channels as referred to in the BOLT 2 Lightning Network specification—have been a long time coming. This protocol has already been adopted by the Eclair and Core Lightning (CLN) node implementations. We take a closer look at the progress and architecture of the dual-funded channel implementation in the Lightning Development Kit (LDK) and what makes this challenging.
Relevant Background on LDK’s Architecture
You may be aware that the base LDK project—the lightning crate, its language bindings, and friends—is a library for plugging into the Lightning Network, handling all the intricacies of the specification and leaving a significant amount of flexibility for the developer integrating LDK into their application. Consequently, LDK must make more abstractions around interfaces that are not made by Lightning Network node implementations such as LND, CLN, and Eclair, which act as standalone daemons out of the box.
One instance of such an abstraction is making few assumptions about the environment in which the application lives. It may be embedded, as is the case for Validating Lightning Signer (VLS), and would need to work in a no-std world. In other words, LDK does not expect an operating system.
Interfacing with The World
LDK provides the interfaces (traits in Rust) that developers can implement to provide essential functionality needed for the operation of a Lightning Network node, such as persistence, signing, and networking. LDK also makes no assumptions around the existence of a particular Bitcoin node implementation such as Bitcoin Core or btcd, so it cannot rely on ‘ye old faithful and familiar Bitcoin Core RPCs to peek at what’s happening at the base layer. All communication into and outside LDK’s very specific view of the Lightning Network protocol is via the shuffling of bytes through a developer’s implementation of the required interfaces.
Event Handling
One unique example of a trait in LDK that a developer would need to implement is the EventHandler:
The single required method, handle_event(), must be implemented to handle the various user-actionable events generated by LDK, of which there are many variants (currently for v0.1.1):
Specifically looking at the OpenChannelRequest variant, if we do not specify the automatic acceptance of inbound channels under certain conditions, we receive an event with all the relevant information about the proposed inbound channel from a peer.
In particular, the channel_negotiation_type field would specify whether this request is for an inbound dual-funded channel or the push_msats value for an inbound V1 channel.
Background Tasks
LDK makes progress in state via user-called methods and messages received from peers. There are still many tasks that need to be completed in the background, not dependent on receiving peer messages or user method calls. These include management of feerate estimates for our outbound channels, managing peer connectivity, expiring outbound payments, and so forth. In order to make progress in this case, the ChannelManager::timer_tick_occurred() method must be called every minute.
The Current Implementation Design and Status of Dual-Funded Channels in LDK
Although still in progress, there are some design considerations we can discuss about LDK’s implementation of dual-funded channels.
The Interactive Transaction Constructor
Dual-funded channels and splicing both depend on the interactive transaction construction protocol, specified in BOLT 2. In LDK, this is implemented as a type-safe state machine that is driven to a completely constructed funding transaction that is ready for interactive signing.
BOLT 2 also provides an example of a message exchange between the channel initiator and channel acceptor during interactive transaction construction:
LDK encapsulates this state machine in the internal InteractiveTxConstructor struct, which is held by pending dual-funded channels.
Public APIs for Dual-Funded Channels
In order to support dual-funded channels, we need to introduce some public methods to ChannelManager for creating and accepting dual-funded channels and signing their funding transactions. Opening dual-funded channels will use a new create_dual_funded_channel method where a user will need to provide the funding inputs to be added during interactive transaction construction. Similarly, for accepting dual-funded channels where the acceptor will be contributing, inputs would also need to be provided to that method.
A major difference comes up during signing the funding transaction. With V1 channels, we would expect the user to create and sign the funding transaction based on information we give them, such as the funding outpoint. However, in the case of dual-funded channels, the funding transaction is interactively constructed, and we need to provide the unsigned transaction to the user via an event so that they can sign the inputs they provided and provide those witnesses back to ChannelManager for that specific ChannelId. For either case, LDK handles the broadcasting of the funding transaction when it is safe to do so.
The Current Status of Dual-Funding in LDK
A lot of work has gone into internal refactoring in Channel and ChannelManager to pave the way for dual-funding (and splicing) support. LDK—being a library by nature—made some of the dual-funding implementation details a little tricky, with some outstanding challenges, such as interactive RBF, still needing to be solved.
There is internal support for accepting dual-funded channels at the moment (since v0.1.0), but it is cfg-flagged at the moment as some quirks are being ironed out. Full support for creating and accepting dual-funded channels—without RBF support—can be expected in LDK v0.2.0.