Skip to content

Commit dfd8295

Browse files
author
Conor Okus
committed
Updated Closing Channel Tutorial
1 parent cfa19e2 commit dfd8295

File tree

1 file changed

+64
-28
lines changed

1 file changed

+64
-28
lines changed

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

Lines changed: 64 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Closing a Channel
22

3-
Close Channel
3+
Close Channel.
44

55
<CodeSwitcher :languages="{rust:'Rust', java:'Java', swift:'Swift'}">
66
<template v-slot:rust>
@@ -32,7 +32,7 @@ if res!.isOk() {
3232
</CodeSwitcher>
3333

3434

35-
Claim Funds using Custom KeysManager. (Single Fees)
35+
Claim Funds directly into the BDK wallet using Custom KeysManager.
3636

3737
<CodeSwitcher :languages="{rust:'Rust', java:'Java', swift:'Swift'}">
3838
<template v-slot:rust>
@@ -52,41 +52,58 @@ Claim Funds using Custom KeysManager. (Single Fees)
5252
<template v-slot:swift>
5353

5454
```Swift
55-
// Custom KeysManager to get funds directly back to the BDK wallet after Channel Close
55+
import Foundation
56+
import LightningDevKit
57+
import BitcoinDevKit
58+
5659
class MyKeysManager {
57-
let keysManager: KeysManager
58-
let signerProvider: MySignerProvider
60+
let inner: KeysManager
5961
let wallet: BitcoinDevKit.Wallet
62+
let signerProvider: MySignerProvider
6063

6164
init(seed: [UInt8], startingTimeSecs: UInt64, startingTimeNanos: UInt32, wallet: BitcoinDevKit.Wallet) {
62-
self.keysManager = KeysManager(seed: seed, startingTimeSecs: startingTimeSecs, startingTimeNanos: startingTimeNanos)
65+
self.inner = KeysManager(seed: seed, startingTimeSecs: startingTimeSecs, startingTimeNanos: startingTimeNanos)
6366
self.wallet = wallet
6467
signerProvider = MySignerProvider()
6568
signerProvider.myKeysManager = self
6669
}
70+
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
83+
}
84+
return true
85+
}
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+
}
6795
}
6896

69-
// Custom SignerProvider to override getDestinationScript() and getShutdownScriptpubkey()
7097
class MySignerProvider: SignerProvider {
7198
weak var myKeysManager: MyKeysManager?
72-
override func deriveChannelSigner(channelValueSatoshis: UInt64, channelKeysId: [UInt8]) -> Bindings.WriteableEcdsaChannelSigner {
73-
return myKeysManager!.keysManager.asSignerProvider().deriveChannelSigner(channelValueSatoshis: channelValueSatoshis, channelKeysId: channelKeysId)
74-
}
75-
76-
override func generateChannelKeysId(inbound: Bool, channelValueSatoshis: UInt64, userChannelId: [UInt8]) -> [UInt8] {
77-
return myKeysManager!.keysManager.asSignerProvider().generateChannelKeysId(inbound: inbound, channelValueSatoshis: channelValueSatoshis, userChannelId: userChannelId)
78-
}
79-
80-
override func readChanSigner(reader: [UInt8]) -> Bindings.Result_WriteableEcdsaChannelSignerDecodeErrorZ {
81-
return myKeysManager!.keysManager.asSignerProvider().readChanSigner(reader: reader)
82-
}
8399

100+
// We return the destination and shutdown scripts derived by the BDK wallet.
84101
override func getDestinationScript() -> Bindings.Result_ScriptNoneZ {
85102
do {
86103
let address = try myKeysManager!.wallet.getAddress(addressIndex: .new)
87104
return Bindings.Result_ScriptNoneZ.initWithOk(o: address.address.scriptPubkey().toBytes())
88105
} catch {
89-
return myKeysManager!.keysManager.asSignerProvider().getDestinationScript()
106+
return .initWithErr()
90107
}
91108
}
92109

@@ -137,19 +154,38 @@ class MySignerProvider: SignerProvider {
137154
return Bindings.Result_ShutdownScriptNoneZ.initWithOk(o: res.getValue()!)
138155
}
139156
}
140-
return myKeysManager!.keysManager.asSignerProvider().getShutdownScriptpubkey()
157+
return .initWithErr()
141158
} catch {
142-
return myKeysManager!.keysManager.asSignerProvider().getShutdownScriptpubkey()
159+
return .initWithErr()
143160
}
144161
}
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)
181+
}
145182
}
146-
147183
```
148184

149185
</template>
150186
</CodeSwitcher>
151187

152-
Claim Funds using Events. (Double Fees)
188+
Handle Spendable Outputs event.
153189

154190
<CodeSwitcher :languages="{rust:'Rust', java:'Java', swift:'Swift'}">
155191
<template v-slot:rust>
@@ -171,21 +207,21 @@ Claim Funds using Events. (Double Fees)
171207
```Swift
172208
func handleEvent(event: Event) {
173209
if let event = event.getValueAsSpendableOutputs() {
210+
print("handleEvent: trying to spend output")
174211
let outputs = event.getOutputs()
175212
do {
176-
let address = // Get an address to transfer the funds
213+
let address = ldkManager!.bdkManager.getAddress(addressIndex: .new)!
177214
let script = try Address(address: address).scriptPubkey().toBytes()
178-
let res = ldkManager.keysManager.spendSpendableOutputs(
215+
let res = ldkManager!.myKeysManager.spendSpendableOutputs(
179216
descriptors: outputs,
180217
outputs: [],
181218
changeDestinationScript: script,
182219
feerateSatPer1000Weight: 1000,
183-
locktime: nil
184-
)
220+
locktime: nil)
185221
if res.isOk() {
186222
var txs: [[UInt8]] = []
187223
txs.append(res.getValue()!)
188-
ldkManager.broadcaster.broadcastTransactions(txs: txs)
224+
ldkManager!.broadcaster.broadcastTransactions(txs: txs)
189225
}
190226
} catch {
191227
print(error.localizedDescription)

0 commit comments

Comments
 (0)