Skip to content

Watchtower support: expose channel monitor data for external watchtower implementations #813

@FreeOnlineUser

Description

@FreeOnlineUser

Summary

We've implemented an LDK-to-LND watchtower bridge that lets ldk-node mobile clients use existing LND watchtower infrastructure for channel protection. This is running in production on Android (GrapheneOS) with verified blob pushes to a live LND tower.

We'd like to upstream the ldk-node side of this, which is minimal and opt-in.

Problem

Mobile Lightning nodes are intermittently offline. LND has mature watchtower infrastructure deployed on thousands of home nodes (Umbrel, Start9, RaspiBlitz), but there's no way for ldk-node clients to use it.

What we built

In ldk-node (proposed upstream, ~300 lines):

  • WatchtowerPersister: wraps MonitorUpdatingPersister, intercepts persist_new_channel and update_persisted_channel callbacks to capture commitment data in real time
  • 3 new methods on Node:
    • watchtower_drain_justice_blobs() -- returns pending commitment data, clears queue
    • watchtower_set_sweep_address(bytes) -- sets the address for justice tx outputs
    • watchtower_list_monitors() -- lists monitored channel points
  • UniFFI bindings (Kotlin/Swift)
  • Zero behavior change when not used. Wrapper delegates all existing Persist calls untouched

Changes are 2 files + 1 new file:

  • src/watchtower.rs (new) -- WatchtowerPersister + types
  • src/types.rs -- type alias update (2 lines)
  • src/builder.rs -- wrap persister (3 lines)

Separate crate (not proposed for upstream):

  • ldk-watchtower-client: LND wire protocol (Brontide/Noise_XK with secp256k1), justice blob encryption, embedded Tor via Arti for direct .onion connections

Proven in production

  • Bitcoin Pocket Node: Android app running full bitcoind + ldk-node
  • Justice blobs successfully pushed to live LND 0.20.0-beta tower on Umbrel
  • Tor connection verified on Pixel 9 (GrapheneOS) -- no SSH tunnel, no Orbot
  • Custom Brontide implementation verified against BOLT 8 test vectors

Questions for maintainers

  1. Is this the right abstraction level? We expose raw commitment data rather than implementing a specific watchtower protocol in ldk-node itself
  2. Should WatchtowerPersister be opt-in via a builder flag, or always-on with zero overhead when unused?
  3. Any concerns about the wrapper pattern around MonitorUpdatingPersister?

Happy to adjust the approach based on feedback. Fork with implementation: FreeOnlineUser/ldk-node (6 commits, all tests pass)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions