Skip to content

Commit 72e25c1

Browse files
author
Conor Okus
committed
Adds closing channel page
1 parent a3b54e1 commit 72e25c1

File tree

3 files changed

+76
-162
lines changed

3 files changed

+76
-162
lines changed

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

Lines changed: 73 additions & 159 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,37 @@
11
# Closing a Channel
22

3-
Close Channel.
3+
Begins the process of closing a channel. After this call (plus some timeout), no new HTLCs will be accepted on the given channel, and after additional timeout/the closing of all pending HTLCs, the channel will be closed on chain.
44

5-
<CodeSwitcher :languages="{rust:'Rust', java:'Java', swift:'Swift'}">
5+
<CodeSwitcher :languages="{rust:'Rust', kotlin:'Kotlin', swift:'Swift'}">
66
<template v-slot:rust>
77

88
```rust
9-
// TODO: Add Rust Code Here
9+
let channel_id = channel_manager
10+
.list_channels()
11+
.iter()
12+
.find(|channel| channel.user_id == user_id)
13+
.expect("ERROR: Channel not found")
14+
.channel_id;
15+
16+
// Example: Cooperative close
17+
channel_manager.close_channel(&channel_id).expect("ERROR: Failed to close channel");
18+
19+
// Example: Unilateral close
20+
channel_manager.force_close_channel(&channel_id).expect("ERROR: Failed to close channel");
1021
```
11-
1222
</template>
13-
<template v-slot:java>
23+
<template v-slot:kotlin>
24+
25+
```kotlin
26+
val res = channelManager!!.close_channel(channelId, pubKey)
27+
28+
if (res is Result_NoneAPIErrorZ.Result_NoneAPIErrorZ_Err) {
29+
// Handle error
30+
}
1431

15-
```java
16-
// TODO: Add Java Code Here
32+
if (res.is_ok) {
33+
// Handle successful close
34+
}
1735
```
1836

1937
</template>
@@ -32,182 +50,78 @@ if res!.isOk() {
3250
</CodeSwitcher>
3351

3452

35-
Claim Funds directly into the BDK wallet using Custom KeysManager.
53+
To claim Funds directly into a custom wallet like BDK wallet using a custom `KeysManager` see the [Key Management](/key_management.md) guide for more info.
54+
55+
# SpendableOutputs Event Handling
3656

37-
<CodeSwitcher :languages="{rust:'Rust', java:'Java', swift:'Swift'}">
57+
<CodeSwitcher :languages="{rust:'Rust', kotlin:'Kotlin', swift:'Swift'}">
3858
<template v-slot:rust>
3959

4060
```rust
41-
// TODO: Add Rust Code Here
42-
```
43-
44-
</template>
45-
<template v-slot:java>
61+
Event::SpendableOutputs { outputs, channel_id: _ } => {
62+
// SpendableOutputDescriptors, of which outputs is a vec of, are critical to keep track
63+
// of! While a `StaticOutput` descriptor is just an output to a static, well-known key,
64+
// other descriptors are not currently ever regenerated for you by LDK. Once we return
65+
// from this method, the descriptor will be gone, and you may lose track of some funds.
66+
//
67+
// Here we simply persist them to disk, with a background task running which will try
68+
// to spend them regularly (possibly duplicatively/RBF'ing them). These can just be
69+
// treated as normal funds where possible - they are only spendable by us and there is
70+
// no rush to claim them.
71+
for output in outputs {
72+
let key = hex_utils::hex_str(&keys_manager.get_secure_random_bytes());
73+
// Note that if the type here changes our read code needs to change as well.
74+
let output: SpendableOutputDescriptor = output;
75+
fs_store.write(PENDING_SPENDABLE_OUTPUT_DIR, "", &key, &output.encode()).unwrap();
76+
}
77+
}
4678

47-
```java
48-
// TODO: Add Java Code Here
4979
```
5080

5181
</template>
52-
<template v-slot:swift>
82+
<template v-slot:kotlin>
83+
84+
```kotlin
85+
// Example where we spend straight to our BDK based wallet
86+
if (event is Event.SpendableOutputs) {
87+
val outputs = event.outputs
88+
try {
89+
val address = OnchainWallet.getNewAddress()
90+
val script = Address(address).scriptPubkey().toBytes().toUByteArray().toByteArray()
91+
val txOut: Array<TxOut> = arrayOf()
92+
val res = keysManager?.spend_spendable_outputs(
93+
outputs,
94+
txOut,
95+
script,
96+
1000,
97+
Option_u32Z.None.none()
98+
)
5399

54-
```Swift
55-
import Foundation
56-
import LightningDevKit
57-
import BitcoinDevKit
58-
59-
class MyKeysManager {
60-
let inner: KeysManager
61-
let wallet: BitcoinDevKit.Wallet
62-
let signerProvider: MySignerProvider
63-
64-
init(seed: [UInt8], startingTimeSecs: UInt64, startingTimeNanos: UInt32, wallet: BitcoinDevKit.Wallet) {
65-
self.inner = KeysManager(seed: seed, startingTimeSecs: startingTimeSecs, startingTimeNanos: startingTimeNanos)
66-
self.wallet = wallet
67-
signerProvider = MySignerProvider()
68-
signerProvider.myKeysManager = self
69-
}
100+
if (res != null) {
101+
if (res.is_ok) {
102+
val tx = (res as Result_TransactionNoneZ.Result_TransactionNoneZ_OK).res
103+
val txs: Array<ByteArray> = arrayOf()
104+
txs.plus(tx)
70105

71-
// We drop all occurences of `SpendableOutputDescriptor::StaticOutput` (since they will be
72-
// spendable by the BDK wallet) and forward any other descriptors to
73-
// `KeysManager::spend_spendable_outputs`.
74-
//
75-
// Note you should set `locktime` to the current block height to mitigate fee sniping.
76-
// See https://bitcoinops.org/en/topics/fee-sniping/ for more information.
77-
func spendSpendableOutputs(descriptors: [SpendableOutputDescriptor], outputs: [Bindings.TxOut],
78-
changeDestinationScript: [UInt8], feerateSatPer1000Weight: UInt32,
79-
locktime: UInt32?) -> Result_TransactionNoneZ {
80-
let onlyNonStatic: [SpendableOutputDescriptor] = descriptors.filter { desc in
81-
if desc.getValueType() == .StaticOutput {
82-
return false
106+
LDKBroadcaster.broadcast_transactions(txs)
83107
}
84-
return true
85108
}
86-
let res = self.inner.spendSpendableOutputs(
87-
descriptors: onlyNonStatic,
88-
outputs: outputs,
89-
changeDestinationScript: changeDestinationScript,
90-
feerateSatPer1000Weight: feerateSatPer1000Weight,
91-
locktime: locktime
92-
)
93-
return res
94-
}
95-
}
96109

97-
class MySignerProvider: SignerProvider {
98-
weak var myKeysManager: MyKeysManager?
99-
100-
// We return the destination and shutdown scripts derived by the BDK wallet.
101-
override func getDestinationScript() -> Bindings.Result_ScriptNoneZ {
102-
do {
103-
let address = try myKeysManager!.wallet.getAddress(addressIndex: .new)
104-
return Bindings.Result_ScriptNoneZ.initWithOk(o: address.address.scriptPubkey().toBytes())
105-
} catch {
106-
return .initWithErr()
107-
}
108-
}
109-
110-
override func getShutdownScriptpubkey() -> Bindings.Result_ShutdownScriptNoneZ {
111-
do {
112-
let address = try myKeysManager!.wallet.getAddress(addressIndex: .new).address
113-
let payload = address.payload()
114-
if case let .witnessProgram(`version`, `program`) = payload {
115-
let ver: UInt8
116-
switch version {
117-
case .v0:
118-
ver = 0
119-
case .v1:
120-
ver = 1
121-
case .v2:
122-
ver = 2
123-
case .v3:
124-
ver = 3
125-
case .v4:
126-
ver = 4
127-
case .v5:
128-
ver = 5
129-
case .v6:
130-
ver = 6
131-
case .v7:
132-
ver = 7
133-
case .v8:
134-
ver = 8
135-
case .v9:
136-
ver = 9
137-
case .v10:
138-
ver = 10
139-
case .v11:
140-
ver = 11
141-
case .v12:
142-
ver = 12
143-
case .v13:
144-
ver = 13
145-
case .v14:
146-
ver = 14
147-
case .v15:
148-
ver = 15
149-
case .v16:
150-
ver = 16
151-
}
152-
let res = ShutdownScript.newWitnessProgram(version: ver, program: program)
153-
if res.isOk() {
154-
return Bindings.Result_ShutdownScriptNoneZ.initWithOk(o: res.getValue()!)
155-
}
156-
}
157-
return .initWithErr()
158-
} catch {
159-
return .initWithErr()
160-
}
161-
}
162-
163-
// ... and redirect all other trait method implementations to the `inner` `KeysManager`.
164-
override func deriveChannelSigner(channelValueSatoshis: UInt64, channelKeysId: [UInt8]) -> Bindings.WriteableEcdsaChannelSigner {
165-
return myKeysManager!.inner.asSignerProvider().deriveChannelSigner(
166-
channelValueSatoshis: channelValueSatoshis,
167-
channelKeysId: channelKeysId
168-
)
169-
}
170-
171-
override func generateChannelKeysId(inbound: Bool, channelValueSatoshis: UInt64, userChannelId: [UInt8]) -> [UInt8] {
172-
return myKeysManager!.inner.asSignerProvider().generateChannelKeysId(
173-
inbound: inbound,
174-
channelValueSatoshis: channelValueSatoshis,
175-
userChannelId: userChannelId
176-
)
177-
}
178-
179-
override func readChanSigner(reader: [UInt8]) -> Bindings.Result_WriteableEcdsaChannelSignerDecodeErrorZ {
180-
return myKeysManager!.inner.asSignerProvider().readChanSigner(reader: reader)
110+
} catch (e: Exception) {
111+
Log.i(LDKTAG, "Error: ${e.message}")
181112
}
182113
}
183-
```
184114

185-
</template>
186-
</CodeSwitcher>
187-
188-
Handle Spendable Outputs event.
189-
190-
<CodeSwitcher :languages="{rust:'Rust', java:'Java', swift:'Swift'}">
191-
<template v-slot:rust>
192-
193-
```rust
194-
// TODO: Add Rust Code Here
195-
```
196-
197-
</template>
198-
<template v-slot:java>
199-
200-
```java
201-
// TODO: Add Java Code Here
202115
```
203116

204117
</template>
205118
<template v-slot:swift>
206119

207120
```Swift
121+
// Example where we spend straight to our BDK based wallet
122+
208123
func handleEvent(event: Event) {
209124
if let event = event.getValueAsSpendableOutputs() {
210-
print("handleEvent: trying to spend output")
211125
let outputs = event.getOutputs()
212126
do {
213127
let address = ldkManager!.bdkManager.getAddress(addressIndex: .new)!

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -125,5 +125,5 @@ let peerNodeIds = peerManager.getPeerNodeIds()
125125

126126
**Dependencies:** `PeerManager`
127127

128-
**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 `NioPeerHandler` bindings](https://github.com/lightningdevkit/ldk-garbagecollected/blob/main/src/main/java/org/ldk/batteries/NioPeerHandler.java),
129-
[Java `PeerManager` bindings](https://github.com/lightningdevkit/ldk-garbagecollected/blob/main/src/main/java/org/ldk/structs/PeerManager.java),
128+
**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),
129+
[Java/Kotlin `PeerManager` bindings](https://github.com/lightningdevkit/ldk-garbagecollected/blob/main/src/main/java/org/ldk/structs/PeerManager.java),

docs/tutorials/building-a-node-with-ldk/setting-up-a-peer-manager.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,4 +62,4 @@ let peer_manager = PeerManager::new(
6262

6363
**Dependencies:** `ChannelManager`, `RoutingMessageHandler`, `KeysManager`, random bytes, `Logger`
6464

65-
**References:** [Rust `PeerManager` docs](https://docs.rs/lightning/*/lightning/ln/peer_handler/struct.PeerManager.html), [Rust `RoutingMessageHandler` docs](https://docs.rs/lightning/*/lightning/ln/msgs/trait.RoutingMessageHandler.html), [Java `PeerManager` bindings](https://github.com/lightningdevkit/ldk-garbagecollected/blob/main/src/main/java/org/ldk/structs/PeerManager.java), [Java `RoutingMessageHandler` bindings](https://github.com/lightningdevkit/ldk-garbagecollected/blob/main/src/main/java/org/ldk/structs/RoutingMessageHandler.java)
65+
**References:** [Rust `PeerManager` docs](https://docs.rs/lightning/*/lightning/ln/peer_handler/struct.PeerManager.html), [Rust `RoutingMessageHandler` docs](https://docs.rs/lightning/*/lightning/ln/msgs/trait.RoutingMessageHandler.html), [Java/Kotlin `PeerManager` bindings](https://github.com/lightningdevkit/ldk-garbagecollected/blob/main/src/main/java/org/ldk/structs/PeerManager.java), [Java/Kotlin `RoutingMessageHandler` bindings](https://github.com/lightningdevkit/ldk-garbagecollected/blob/main/src/main/java/org/ldk/structs/RoutingMessageHandler.java)

0 commit comments

Comments
 (0)