Skip to content

Commit 7539e36

Browse files
ConorOkusclaude
andcommitted
docs(building-a-node): update all samples to LDK 0.2, add TypeScript, drop Swift
Rewrite every code sample across the "Building a Node with LDK" tutorial to the current LDK APIs, verified against docs.rs/lightning/0.2.2, ldk-sample, and ldk-garbagecollected v0.2.0.0: - Rust -> lightning 0.2.2 (the samples were ~0.0.x-era): ConfirmationTarget variants, ChannelManager::new/ReadArgs (+router, +message_router), KeysManager/ChainMonitor/Persist, events, create_bolt11_invoice/ pay_for_bolt11_invoice (old invoice/payment utils removed), process_events_async, list_peers, OutputSpender. - Kotlin -> ldk-java 0.2.0 where the API changed. - Add non-Node.js TypeScript (lightningdevkit 0.2.0-0) to every code-group, with honest notes where the bindings have gaps: pin 0.2.0-0 + WASM init, SocketDescriptor over a WebSocket->TCP proxy for peer connections, and manual event pumping (no BackgroundProcessor). - Remove all Swift tabs (ldk-swift has no 0.1/0.2 release). - Refresh implementation notes, dependencies, and reference links (docs.rs 0.2.2 / ldk-garbagecollected v0.2.0.0 / TS bindings). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 60bf1aa commit 7539e36

10 files changed

Lines changed: 905 additions & 815 deletions

docs/building-a-node-with-ldk/closing-a-channel.md

Lines changed: 49 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -5,22 +5,29 @@ Begins the process of closing a channel. After this call (plus some timeout), no
55
::: code-group
66

77
```rust [Rust]
8-
let channel_id = channel_manager
8+
// Both close methods now require the counterparty's node id, so grab the full
9+
// ChannelDetails. Note the field is `user_channel_id` (a u128) in 0.2.
10+
let channel = channel_manager
911
.list_channels()
10-
.iter()
11-
.find(|channel| channel.user_id == user_id)
12-
.expect("ERROR: Channel not found")
13-
.channel_id;
12+
.into_iter()
13+
.find(|channel| channel.user_channel_id == user_channel_id)
14+
.expect("ERROR: Channel not found");
15+
let channel_id = channel.channel_id;
16+
let counterparty_node_id = channel.counterparty.node_id;
1417

1518
// Example: Cooperative close
16-
channel_manager.close_channel(&channel_id).expect("ERROR: Failed to close channel");
17-
18-
// Example: Unilateral close
19-
channel_manager.force_close_channel(&channel_id).expect("ERROR: Failed to close channel");
19+
channel_manager
20+
.close_channel(&channel_id, &counterparty_node_id)
21+
.expect("ERROR: Failed to close channel");
22+
23+
// Example: Unilateral close (renamed; now requires an error-message string)
24+
channel_manager
25+
.force_close_broadcasting_latest_txn(&channel_id, &counterparty_node_id, "manually force-closed".to_string())
26+
.expect("ERROR: Failed to force-close channel");
2027
```
2128

2229
```kotlin [Kotlin]
23-
val res = channelManager.close_channel(channelId, pubKey)
30+
val res = channelManager.close_channel(channelId, counterpartyNodeId)
2431

2532
if (res is Result_NoneAPIErrorZ.Result_NoneAPIErrorZ_Err) {
2633
// Handle error
@@ -31,12 +38,15 @@ if (res.is_ok) {
3138
}
3239
```
3340

34-
```Swift [Swift]
35-
let channelId: [UInt8] = // Add Channel Id in bytes
36-
let counterpartyNodeId: [UInt8] = // Add Counterparty Node Id in bytes
37-
let res = channelManager.closeChannel(channelId: channelId, counterpartyNodeId: counterpartyNodeId)
38-
if res!.isOk() {
39-
// Channel Closed
41+
```typescript [TypeScript]
42+
import * as ldk from "lightningdevkit";
43+
44+
// channelId: ChannelId, counterpartyNodeId: Uint8Array (from the ChannelDetails)
45+
const res = channelManager.close_channel(channelId, counterpartyNodeId);
46+
if (res instanceof ldk.Result_NoneAPIErrorZ_Err) {
47+
// Handle error
48+
} else {
49+
// Handle successful close
4050
}
4151
```
4252

@@ -78,58 +88,44 @@ if (event is Event.SpendableOutputs) {
7888
val address = OnchainWallet.getNewAddress()
7989
val script = Address(address).scriptPubkey().toBytes().toUByteArray().toByteArray()
8090
val txOut: Array<TxOut> = arrayOf()
81-
val res = keysManager?.spend_spendable_outputs(
91+
// `spend_spendable_outputs` moved from KeysManager onto the OutputSpender
92+
// trait, and gained a trailing `locktime` argument.
93+
val res = keysManager?.as_OutputSpender()?.spend_spendable_outputs(
8294
outputs,
8395
txOut,
8496
script,
8597
1000,
86-
Option_u32Z.None.none()
98+
Option_u32Z.none() // locktime
8799
)
88100

89-
if (res != null) {
90-
if (res.is_ok) {
91-
val tx = (res as Result_TransactionNoneZ.Result_TransactionNoneZ_OK).res
92-
val txs: Array<ByteArray> = arrayOf()
93-
txs.plus(tx)
94-
95-
LDKBroadcaster.broadcast_transactions(txs)
96-
}
101+
if (res != null && res.is_ok) {
102+
val tx = (res as Result_TransactionNoneZ.Result_TransactionNoneZ_OK).res
103+
LDKBroadcaster.broadcast_transactions(arrayOf(tx))
97104
}
98-
99105
} catch (e: Exception) {
100106
Log.i(LDKTAG, "Error: ${e.message}")
101107
}
102108
}
103-
104109
```
105110

106-
```Swift [Swift]
107-
// Example where we spend straight to our BDK based wallet
108-
func handleEvent(event: Event) {
109-
if let event = event.getValueAsSpendableOutputs() {
110-
let outputs = event.getOutputs()
111-
do {
112-
let address = ldkManager!.bdkManager.getAddress(addressIndex: .new)!
113-
let network = ldkManager!.network == .Testnet ? BitcoinDevKit.Network.testnet : BitcoinDevKit.Network.regtest
114-
let script = try Address(address: address, network: network).scriptPubkey().toBytes()
115-
let res = ldkManager!.myKeysManager.spendSpendableOutputs(
116-
descriptors: outputs,
117-
outputs: [],
118-
changeDestinationScript: script,
119-
feerateSatPer1000Weight: 1000,
120-
locktime: nil)
121-
if res.isOk() {
122-
var txs: [[UInt8]] = []
123-
txs.append(res.getValue()!)
124-
ldkManager!.broadcaster.broadcastTransactions(txs: txs)
125-
}
126-
} catch {
127-
print(error.localizedDescription)
128-
}
129-
}
111+
```typescript [TypeScript]
112+
import * as ldk from "lightningdevkit";
113+
114+
if (event instanceof ldk.Event_SpendableOutputs) {
115+
// `spend_spendable_outputs` is on the OutputSpender trait.
116+
const res = keysManager.as_OutputSpender().spend_spendable_outputs(
117+
event.outputs, // SpendableOutputDescriptor[]
118+
[], // additional TxOut[]
119+
changeDestinationScript, // Uint8Array (your change scriptPubKey)
120+
1000, // feerate_sat_per_1000_weight
121+
ldk.Option_u32Z.constructor_none() // locktime
122+
);
123+
if (res instanceof ldk.Result_TransactionNoneZ_OK) {
124+
txBroadcaster.broadcast_transactions([res.res]);
125+
}
130126
}
131127
```
132128

133129
:::
134130

135-
**References:** [Rust `SpendableOutputs` docs](https://docs.rs/lightning/*/lightning/events/enum.Event.html#variant.SpendableOutputs), [Java/Kotlin `SpendableOutputs` bindings](https://github.com/lightningdevkit/ldk-garbagecollected/blob/main/src/main/java/org/ldk/structs/Event.java#L802)
131+
**References:** [Rust `SpendableOutputs` docs](https://docs.rs/lightning/0.2.2/lightning/events/enum.Event.html#variant.SpendableOutputs), [Rust `OutputSpender` docs](https://docs.rs/lightning/0.2.2/lightning/sign/trait.OutputSpender.html), [Java/Kotlin `Event` bindings](https://github.com/lightningdevkit/ldk-garbagecollected/blob/v0.2.0.0/src/main/java/org/ldk/structs/Event.java), [TypeScript `OutputSpender` bindings](https://github.com/lightningdevkit/ldk-garbagecollected/blob/v0.2.0.0/ts/structs/OutputSpender.mts)

docs/building-a-node-with-ldk/connect-to-peers.md

Lines changed: 65 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ In this section you'll learn how to join the lightning network.
44

55
Firstly we need to have the ability to do high performance I/O operations. LDK provides default implementations for initializing all of your networking needs. If you are using Rust, you can use our simple socket handling library `lightning_net_tokio`. In Kotlin/Java you can use the `NioPeerHandler` which uses Java's NIO I/O interface.
66

7+
In TypeScript there is no networking module for non-Node.js (browser) environments: WASM cannot open raw TCP sockets. Instead you implement a `SocketDescriptor` that bridges LDK to a transport you do have — typically a `WebSocket` talking to a WebSocket-to-TCP proxy you run server-side — and feed bytes to the `PeerManager` yourself. (Node.js users can use the separate [`lightningdevkit-node-net`](https://www.npmjs.com/package/lightningdevkit-node-net) package instead.)
8+
79
**What it's used for**: making peer connections, facilitating peer data to and from LDK
810

911
::: code-group
@@ -13,14 +15,15 @@ use lightning_net_tokio; // use LDK's sample networking module
1315

1416
let listen_port = 9735;
1517
let listener = tokio::net::TcpListener::bind(format!("0.0.0.0:{}", listen_port))
16-
.await.unwrap()
18+
.await.unwrap();
1719
loop {
1820
let tcp_stream = listener.accept().await.unwrap().0;
21+
let peer_manager = peer_manager.clone();
1922
tokio::spawn(async move {
2023
// Use LDK's supplied networking battery to facilitate inbound
2124
// connections.
2225
lightning_net_tokio::setup_inbound(
23-
&peer_manager,
26+
peer_manager,
2427
tcp_stream.into_std().unwrap(),
2528
)
2629
.await;
@@ -34,10 +37,36 @@ val port = 9777
3437
nioPeerHandler.bind_listener(InetSocketAddress("127.0.0.1", port))
3538
```
3639

37-
```Swift [Swift]
38-
let peerHandler = channelManagerConstructor.getTCPPeerHandler()
39-
let port = 9777
40-
peerHandler.bind(address: "127.0.0.1", port: port)
40+
```typescript [TypeScript]
41+
import * as ldk from "lightningdevkit";
42+
43+
// Bridge a transport (here a WebSocket to a WS->TCP proxy) to LDK by
44+
// implementing SocketDescriptor. There is no raw TCP in the browser.
45+
function makeSocketDescriptor(ws: WebSocket, id: bigint): ldk.SocketDescriptor {
46+
return ldk.SocketDescriptor.new_impl({
47+
// Return how many bytes you accepted; buffer the rest if back-pressured.
48+
send_data(data: Uint8Array, _resume_read: boolean): number {
49+
ws.send(data);
50+
return data.length;
51+
},
52+
disconnect_socket(): void { ws.close(); },
53+
eq(other: ldk.SocketDescriptor): boolean { return other.hash() === id; },
54+
hash(): bigint { return id; },
55+
} as ldk.SocketDescriptorInterface);
56+
}
57+
58+
// On an inbound connection from the proxy:
59+
const descriptor = makeSocketDescriptor(ws, 1n);
60+
peerManager.new_inbound_connection(
61+
descriptor,
62+
ldk.Option_SocketAddressZ.constructor_none()
63+
);
64+
65+
// Forward every chunk you receive from the socket into LDK, then flush:
66+
ws.onmessage = (ev) => {
67+
peerManager.read_event(descriptor, new Uint8Array(ev.data));
68+
peerManager.process_events();
69+
};
4170
```
4271

4372
:::
@@ -47,7 +76,7 @@ Connections to other peers are established with `PeerManager`. You'll need to kn
4776
::: code-group
4877

4978
```rust [Rust]
50-
match lightning_net_tokio::connect_outbound(Arc::clone(&peer_manager), pubkey, address).await {
79+
match lightning_net_tokio::connect_outbound(peer_manager.clone(), pubkey, address).await {
5180
Some(connection_closed_future) => {
5281
let mut connection_closed_future = Box::pin(connection_closed_future);
5382
loop {
@@ -59,8 +88,9 @@ match lightning_net_tokio::connect_outbound(Arc::clone(&peer_manager), pubkey, a
5988
std::task::Poll::Pending => {}
6089
}
6190

62-
// Wait for the handshake to complete.
63-
match peer_manager.get_peer_node_ids().iter().find(|id| **id == pubkey) {
91+
// Wait for the handshake to complete. `get_peer_node_ids` was
92+
// replaced by `list_peers`, which returns rich `PeerDetails`.
93+
match peer_manager.list_peers().iter().find(|p| p.counterparty_node_id == pubkey) {
6494
Some(_) => break,
6595
None => tokio::time::sleep(std::time::Duration::from_millis(10)).await,
6696
}
@@ -76,32 +106,42 @@ try {
76106
val address: SocketAddress = InetSocketAddress(hostname, port)
77107
nioPeerHandler.connect(pubkeyHex.toByteArray(), address, 5555)
78108

79-
// The peer's pubkey will be present in the list of peer ids.
109+
// The peer's pubkey will be present in the list of peers. (`get_peer_node_ids`
110+
// was removed in favour of `list_peers`, which returns `PeerDetails`.)
80111
val peerManager: PeerManager = channelManagerConstructor.peer_manager
81-
val peerNodeIds = peerManager._peer_node_ids
112+
val peerNodeIds = peerManager.list_peers().map { it._counterparty_node_id }
82113

83114
} catch (e: IOException) {
84115
// Handle failure when connecting to a peer.
85116

86117
}
118+
```
87119

88-
````
89-
90-
```Swift [Swift]
91-
// Connect and wait for the handshake to complete.
92-
let pubKey = // Insert code to retrieve peer's pubKey as byte array
93-
let address = // Insert code to retrieve peer's address
94-
let port = // Insert code to retrieve peer's port
95-
let _ = peerHandler.connect(address: address, port: port, theirNodeId: pubKey)
120+
```typescript [TypeScript]
121+
import * as ldk from "lightningdevkit";
122+
123+
// `pubkey` is the peer's 33-byte node id. Kick off the outbound handshake;
124+
// `new_outbound_connection` returns the first bytes to send to the peer.
125+
const descriptor = makeSocketDescriptor(ws, 2n);
126+
const initialSend = peerManager.new_outbound_connection(
127+
pubkey,
128+
descriptor,
129+
ldk.Option_SocketAddressZ.constructor_none()
130+
);
131+
if (initialSend instanceof ldk.Result_CVec_u8ZPeerHandleErrorZ_OK) {
132+
ws.send(initialSend.res);
133+
}
96134

97-
// The peer's pubkey will be present in the list of peer ids.
98-
let peerManager: PeerManager = channelManagerConstructor.peerManager
99-
let peerNodeIds = peerManager.getPeerNodeIds()
100-
````
135+
// Feed received bytes in (as in the inbound example), then check the handshake
136+
// completed by looking for the peer in the list. (`get_peer_node_ids` was
137+
// removed in favour of `list_peers`, which returns `PeerDetails`.)
138+
const connected = peerManager
139+
.list_peers()
140+
.some((p) => p.get_counterparty_node_id().toString() === pubkey.toString());
141+
```
101142

102143
:::
103144

104145
**Dependencies:** `PeerManager`
105146

106-
**References:** [Rust `lightning-net-tokio` docs](https://docs.rs/lightning-net-tokio/*/lightning_net_tokio/), [Rust `PeerManager` docs](https://docs.rs/lightning/*/lightning/ln/peer_handler/struct.PeerManager.html), [Java/Kotlin `NioPeerHandler` bindings](https://github.com/lightningdevkit/ldk-garbagecollected/blob/main/src/main/java/org/ldk/batteries/NioPeerHandler.java),
107-
[Java/Kotlin `PeerManager` bindings](https://github.com/lightningdevkit/ldk-garbagecollected/blob/main/src/main/java/org/ldk/structs/PeerManager.java),
147+
**References:** [Rust `lightning-net-tokio` docs](https://docs.rs/lightning-net-tokio/0.2.0/lightning_net_tokio/), [Rust `PeerManager` docs](https://docs.rs/lightning/0.2.2/lightning/ln/peer_handler/struct.PeerManager.html), [Java/Kotlin `NioPeerHandler` bindings](https://github.com/lightningdevkit/ldk-garbagecollected/blob/v0.2.0.0/src/main/java/org/ldk/batteries/NioPeerHandler.java), [Java/Kotlin `PeerManager` bindings](https://github.com/lightningdevkit/ldk-garbagecollected/blob/v0.2.0.0/src/main/java/org/ldk/structs/PeerManager.java), [TypeScript `SocketDescriptor` bindings](https://github.com/lightningdevkit/ldk-garbagecollected/blob/v0.2.0.0/ts/structs/SocketDescriptor.mts)

docs/building-a-node-with-ldk/handling-events.md

Lines changed: 37 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -7,55 +7,66 @@ To start handling events in your application, run:
77
::: code-group
88

99
```rust [Rust]
10-
use lightning::util::events::{Event};
10+
use lightning::events::Event;
1111

12-
// In the event handler passed to BackgroundProcessor::start
12+
// In the async event handler passed to lightning_background_processor::process_events_async
1313
match event {
14-
Event::PaymentSent { payment_preimage } => {
14+
Event::PaymentSent { payment_preimage, payment_hash, .. } => {
1515
// Handle successful payment
1616
}
17-
Event::PaymentFailed { payment_hash, rejected_by_dest } => {
17+
// Note: in 0.2 `PaymentFailed` carries a `payment_id` and an optional
18+
// `reason` — the old `rejected_by_dest` field is gone.
19+
Event::PaymentFailed { payment_id, payment_hash, reason } => {
1820
// Handle failed payment
1921
}
2022
Event::FundingGenerationReady { .. } => {
2123
// Generate the funding transaction for the channel
2224
}
25+
// The Event enum has many more variants — handle the rest as needed.
26+
_ => {}
2327
}
2428
```
2529

2630
```kotlin [Kotlin]
27-
import org.ldk.structs.Event
31+
import org.ldk.structs.Event
2832

33+
// In the `ChannelManagerConstructor.EventHandler` you pass to `chain_sync_completed`
2934
if (event is Event.PaymentSent) {
30-
// Handle successful payment
35+
// Handle successful payment
3136
}
3237

3338
if (event is Event.PaymentFailed) {
34-
// Handle failed payment
39+
// Handle failed payment
3540
}
3641

3742
if (event is Event.FundingGenerationReady) {
38-
// Create a funding tx to be broadcast
39-
}
40-
41-
````
42-
43-
```Swift [Swift]
44-
import LightningDevKit
45-
46-
if let event = event.getValueAsPaymentSent() {
47-
// Handle successful payment
48-
}
49-
50-
if let event = event.getValueAsPaymentFailed() {
51-
// Handle failed payment
43+
// Create a funding tx to be broadcast
5244
}
45+
```
5346

54-
if let event = event.getValueAsFundingGenerationReady() {
55-
// Create a funding tx to be broadcast
56-
}
57-
````
47+
```typescript [TypeScript]
48+
import * as ldk from "lightningdevkit";
49+
50+
// There is no BackgroundProcessor in the TypeScript bindings — pull events
51+
// yourself by passing an EventHandler to `process_pending_events`.
52+
const handler = ldk.EventHandler.new_impl({
53+
handle_event(event: ldk.Event): ldk.Result_NoneReplayEventZ {
54+
if (event instanceof ldk.Event_PaymentSent) {
55+
// Handle successful payment
56+
} else if (event instanceof ldk.Event_PaymentFailed) {
57+
// Handle failed payment
58+
} else if (event instanceof ldk.Event_FundingGenerationReady) {
59+
// Create a funding tx to be broadcast
60+
}
61+
return ldk.Result_NoneReplayEventZ.constructor_ok();
62+
},
63+
} as ldk.EventHandlerInterface);
64+
65+
// Call this whenever the channel manager / chain monitor signals work is pending.
66+
channelManager.as_EventsProvider().process_pending_events(handler);
67+
chainMonitor.as_EventsProvider().process_pending_events(handler);
68+
```
5869

5970
:::
6071

61-
References: [Rust `Event` docs](https://docs.rs/lightning/*/lightning/events/enum.Event.html), [Java/Kotlin `Event` bindings](https://github.com/lightningdevkit/ldk-garbagecollected/blob/main/src/main/java/org/ldk/structs/Event.java)
72+
References: [Rust `Event` docs](https://docs.rs/lightning/0.2.2/lightning/events/enum.Event.html), [Java/Kotlin `Event` bindings](https://github.com/lightningdevkit/ldk-garbagecollected/blob/v0.2.0.0/src/main/java/org/ldk/structs/Event.java), [TypeScript `Event` bindings](https://github.com/lightningdevkit/ldk-garbagecollected/blob/v0.2.0.0/ts/structs/Event.mts)

0 commit comments

Comments
 (0)