ldk-server-client: Add uniffi bindings#194
Draft
tnull wants to merge 7 commits intolightningdevkit:mainfrom
Draft
ldk-server-client: Add uniffi bindings#194tnull wants to merge 7 commits intolightningdevkit:mainfrom
ldk-server-client: Add uniffi bindings#194tnull wants to merge 7 commits intolightningdevkit:mainfrom
Conversation
Adds opt-in `uniffi` and `uniffi-cli` features so foreign-language consumers (starting with Kotlin for an upcoming Android remote-control app) can call this crate directly instead of reimplementing HMAC-SHA256 auth and gRPC framing. Uses UniFFI's single-crate pattern from the official guide: the bindgen CLI is a [[bin]] target inside this crate, gated on the `uniffi-cli` feature, avoiding the complexity of a separate workspace crate. Wrapper types and the client object are added in follow-up commits. Co-Authored-By: HAL 9000
Defines flat, Kotlin/Swift-friendly analogues for every prost type that the upcoming FFI-exposed client methods will surface: node info, balances, payments (with a flattened `PaymentKindInfo` collapsing the prost `oneof`), channels, peers, send/receive results, decoded invoices and offers, and pagination tokens. Prost types can't be exported directly — they carry `#[derive(::prost::Message)]`, use nested `oneof` submodules, and reach for `prost::bytes::Bytes`. Each wrapper comes with the `From<ProstType>` (and, where both directions apply, `Into<ProstType>`) conversions the exported client will use. Detail deliberately dropped for the MVP scope: - `lightning_balances` and `pending_balances_from_channel_closures` (deep oneof nesting) - `OfferAmount::CurrencyAmount` — flatten to `amount_msat: Option<u64>` - `features` maps and `route_hints` on decoded invoice/offer - Byte-typed `secret` fields on payment kinds Co-Authored-By: HAL 9000
Adds the UniFFI-exported `LdkServerClientUni` object and its async constructor + read-only query methods: get_node_info, get_balances, list_channels, list_peers, list_payments (with optional page_token), get_payment_details. Why a wrapper rather than exporting LdkServerClient directly: the inner reqwest/hyper clients aren't UniFFI-exportable types, but the whole client is `Clone + Send + Sync`, so wrapping it in a `uniffi::Object` satisfies both UniFFI's trait bounds and the Kotlin/Swift `suspend fun` async model (via `async_runtime = "tokio"`). The uniffi dep now enables its `tokio` feature so the generated FFI scaffolding picks up the tokio-aware `#[uniffi::export]` expansion. Co-Authored-By: HAL 9000
Rounds out `LdkServerClientUni` with the full set of methods a wallet UI needs: the unified + per-protocol send flows (unified_send, bolt11_send, bolt12_send, onchain_send), receive flows (bolt11_receive, bolt12_receive, onchain_receive), channel lifecycle (open/close/force-close), peer connect/disconnect, and invoice/offer decoding. UniFFI-side API surface is intentionally simpler than the underlying prost requests: the `route_parameters` configuration on send methods and the `channel_config` on open_channel are hidden (server defaults are what we want); BOLT11 `description` is exposed as `Option<String>` and always attached as a direct description — the description-hash variant isn't useful to a mobile wallet. Co-Authored-By: HAL 9000
Covers the From/Into conversions for every wrapper type: node info (with + without a best block), balances, all PaymentKind oneof variants, channels, peers, all UnifiedSendResult variants (including the protocol-violation error path when the oneof is empty), decoded invoices, and decoded offers (including the flatten-to-None behavior for currency-denominated amounts). Also pins down the graceful-degradation guarantee for unknown protobuf enum values: if the server returns a direction/status code the client doesn't recognize, we fall back to safe defaults (Outbound/Pending) rather than panicking. Co-Authored-By: HAL 9000
Verified end-to-end:
* cross-compile produces ~3.3-4.7 MB libldk_server_client.so per ABI
* generated ldk_server_client.kt has `suspend fun` stubs for every
exported method and data classes/enums for every wrapper type
* jniLibs folder layout matches what the Android Gradle plugin picks up.
Co-Authored-By: HAL 9000
UniFFI's Kotlin generator emits struct-variant errors as subclasses of Throwable, and a constructor property named `message` collides with Throwable.message, producing a compile error on the generated ldk_server_client.kt. Renaming the field to `reason` sidesteps the clash without changing the FFI surface meaningfully. No behavior change; Display output now reads "Invalid request: <reason>" etc. All existing unit tests still pass. Co-Authored-By: HAL 9000
|
👋 Hi! This PR is now in draft status. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Not quite sure we want to add this at this point, but I needed it for a personal project, so vibecoded it. Draft for now.