@@ -6,12 +6,12 @@ However, LDK also allows to customize the way key material and entropy are sourc
6
6
7
7
A ` KeysManager ` can be constructed simply with only a 32-byte seed and some random integers which ensure uniqueness across restarts (defined as ` starting_time_secs ` and ` starting_time_nanos ` ):
8
8
9
- <CodeSwitcher :languages =" {rust:'Rust', java:'Java', kotlin:'Kotlin'} " >
9
+ <CodeSwitcher :languages =" {rust:'Rust', java:'Java', kotlin:'Kotlin', swift:'Swift' } " >
10
10
<template v-slot:rust >
11
11
12
12
``` rust
13
- let mut random_32_bytes = [0 ; 32 ];
14
13
// Fill in random_32_bytes with secure random data, or, on restart, reload the seed from disk.
14
+ let mut random_32_bytes = [0 ; 32 ];
15
15
let start_time = SystemTime :: now (). duration_since (SystemTime :: UNIX_EPOCH ). unwrap ();
16
16
let keys_interface_impl = lightning :: chain :: keysinterface :: KeysManager :: new (& random_32_bytes , start_time . as_secs (), start_time . subsec_nanos ());
17
17
```
@@ -21,8 +21,8 @@ let keys_interface_impl = lightning::chain::keysinterface::KeysManager::new(&ran
21
21
<template v-slot:java >
22
22
23
23
``` java
24
+ // Fill in key_seed with secure random data, or, on restart, reload the seed from disk.
24
25
byte [] key_seed = new byte [32 ];
25
- // Fill in random_32_bytes with secure random data, or, on restart, reload the seed from disk.
26
26
KeysManager keys_manager = KeysManager . of(key_seed,
27
27
System . currentTimeMillis() / 1000 ,
28
28
(int ) (System . currentTimeMillis() * 1000 )
@@ -34,15 +34,32 @@ KeysManager keys_manager = KeysManager.of(key_seed,
34
34
<template v-slot:kotlin >
35
35
36
36
``` kotlin
37
+ // Fill in key_seed with secure random data, or, on restart, reload the seed from disk.
37
38
val key_seed = ByteArray (32 )
38
- // Fill in random_32_bytes with secure random data, or, on restart, reload the seed from disk.
39
39
val keys_manager = KeysManager .of(
40
40
key_seed,
41
41
System .currentTimeMillis() / 1000 , (System .currentTimeMillis() * 1000 ).toInt()
42
42
)
43
43
```
44
44
45
45
</template >
46
+
47
+ <template v-slot:swift >
48
+
49
+ ``` swift
50
+ // Fill in seed with secure random data, or, on restart, reload the seed from disk.
51
+ let seed = [UInt8 ](repeating : 0 , count : 32 )
52
+ let timestampSeconds = UInt64 (NSDate ().timeIntervalSince1970 )
53
+ let timestampNanos = UInt32 (truncating : NSNumber (value : timestampSeconds * 1000 * 1000 ))
54
+ self .myKeysManager = KeysManager (
55
+ seed : seed,
56
+ startingTimeSecs : timestampSeconds,
57
+ startingTimeNanos : timestampNanos
58
+ )
59
+ ```
60
+
61
+ </template >
62
+
46
63
</CodeSwitcher >
47
64
48
65
# Creating a Unified Wallet
@@ -56,7 +73,7 @@ Using a [BDK](https://bitcoindevkit.org/)-based wallet the steps would be as fol
56
73
3 . Derive the private key at ` m/535h ` (or some other custom path). That's 32 bytes and is your starting entropy for your LDK wallet.
57
74
4 . Optional: use a custom ` SignerProvider ` implementation to have the BDK wallet provide the destination and shutdown scripts (see [ Spending On-Chain Funds] ( #spending-on-chain-funds ) ).
58
75
59
- <CodeSwitcher :languages =" {rust:'Rust', java:'Java', kotlin:'Kotlin'} " >
76
+ <CodeSwitcher :languages =" {rust:'Rust', java:'Java', kotlin:'Kotlin', swift:'Swift' } " >
60
77
<template v-slot:rust >
61
78
62
79
``` rust
@@ -127,6 +144,30 @@ val keysManager = KeysManager.of(
127
144
System .currentTimeMillis() / 1000 ,
128
145
(System .currentTimeMillis() * 1000 ).toInt()
129
146
);
147
+ ```
148
+
149
+ </template >
150
+
151
+ <template v-slot:swift >
152
+
153
+ ``` swift
154
+ // Use BDK to create and build the HD wallet
155
+ let mnemonic = try Mnemonic.fromString (mnemonic : " sock lyrics village put galaxy famous pass act ship second diagram pull" )
156
+ // Other supported networks include mainnet (Bitcoin), Regtest, Signet
157
+ let bip32RootKey = DescriptorSecretKey (network : .testnet , mnemonic : mnemonic, password : nil )
158
+ let ldkDerivationPath = try DerivationPath (path : " m/535h" )
159
+ let ldkChild = try bip32RootKey.derive (path : ldkDerivationPath)
160
+ let ldkSeed = ldkChild.secretBytes ()
161
+
162
+ let timestampSeconds = UInt64 (NSDate ().timeIntervalSince1970 )
163
+ let timestampNanos = UInt32 (truncating : NSNumber (value : timestampSeconds * 1000 * 1000 ))
164
+
165
+ // Seed the LDK KeysManager with the private key at m/535h
166
+ let keysManager = KeysManager (
167
+ seed : ldkSeed,
168
+ startingTimeSecs : timestampSeconds,
169
+ startingTimeNanos : timestampNanos
170
+ )
130
171
```
131
172
132
173
</template >
@@ -151,7 +192,7 @@ In order to make the outputs from channel closing spendable by a third-party wal
151
192
152
193
For example, a wrapper based on BDK's [ ` Wallet ` ] ( https://docs.rs/bdk/*/bdk/wallet/struct.Wallet.html ) could look like this:
153
194
154
- <CodeSwitcher :languages =" {rust:'Rust'} " >
195
+ <CodeSwitcher :languages =" {rust:'Rust', swift:'Swift' } " >
155
196
<template v-slot:rust >
156
197
157
198
``` rust
@@ -274,4 +315,134 @@ where
274
315
```
275
316
276
317
</template >
318
+
319
+ <template v-slot:swift >
320
+
321
+ ``` swift
322
+ class MyKeysManager {
323
+ let inner: KeysManager
324
+ let wallet: BitcoinDevKit.Wallet
325
+ let signerProvider: MySignerProvider
326
+
327
+ init (seed : [UInt8 ], startingTimeSecs : UInt64 , startingTimeNanos : UInt32 , wallet : BitcoinDevKit.Wallet) {
328
+ self .inner = KeysManager (seed : seed, startingTimeSecs : startingTimeSecs, startingTimeNanos : startingTimeNanos)
329
+ self .wallet = wallet
330
+ signerProvider = MySignerProvider ()
331
+ signerProvider.myKeysManager = self
332
+ }
333
+
334
+ // We drop all occurences of `SpendableOutputDescriptor::StaticOutput` (since they will be
335
+ // spendable by the BDK wallet) and forward any other descriptors to
336
+ // `KeysManager::spend_spendable_outputs`.
337
+ //
338
+ // Note you should set `locktime` to the current block height to mitigate fee sniping.
339
+ // See https://bitcoinops.org/en/topics/fee-sniping/ for more information.
340
+ func spendSpendableOutputs (descriptors : [SpendableOutputDescriptor], outputs : [Bindings.TxOut],
341
+ changeDestinationScript : [UInt8 ], feerateSatPer1000Weight : UInt32 ,
342
+ locktime : UInt32 ? ) -> Result_TransactionNoneZ {
343
+ let onlyNonStatic: [SpendableOutputDescriptor] = descriptors.filter { desc in
344
+ if desc.getValueType () == .StaticOutput {
345
+ return false
346
+ }
347
+ return true
348
+ }
349
+ let res = self .inner .spendSpendableOutputs (
350
+ descriptors : onlyNonStatic,
351
+ outputs : outputs,
352
+ changeDestinationScript : changeDestinationScript,
353
+ feerateSatPer1000Weight : feerateSatPer1000Weight,
354
+ locktime : locktime
355
+ )
356
+ return res
357
+ }
358
+ }
359
+
360
+ class MySignerProvider : SignerProvider {
361
+ weak var myKeysManager: MyKeysManager?
362
+
363
+ // We return the destination and shutdown scripts derived by the BDK wallet.
364
+ override func getDestinationScript () -> Bindings.Result_ScriptNoneZ {
365
+ do {
366
+ let address = try myKeysManager! .wallet .getAddress (addressIndex : .new )
367
+ return Bindings.Result_ScriptNoneZ .initWithOk (o : address.address .scriptPubkey ().toBytes ())
368
+ } catch {
369
+ return .initWithErr ()
370
+ }
371
+ }
372
+
373
+ override func getShutdownScriptpubkey () -> Bindings.Result_ShutdownScriptNoneZ {
374
+ do {
375
+ let address = try myKeysManager! .wallet .getAddress (addressIndex : .new ).address
376
+ let payload = address.payload ()
377
+ if case let .witnessProgram (`version`, `program`) = payload {
378
+ let ver: UInt8
379
+ switch version {
380
+ case .v0 :
381
+ ver = 0
382
+ case .v1 :
383
+ ver = 1
384
+ case .v2 :
385
+ ver = 2
386
+ case .v3 :
387
+ ver = 3
388
+ case .v4 :
389
+ ver = 4
390
+ case .v5 :
391
+ ver = 5
392
+ case .v6 :
393
+ ver = 6
394
+ case .v7 :
395
+ ver = 7
396
+ case .v8 :
397
+ ver = 8
398
+ case .v9 :
399
+ ver = 9
400
+ case .v10 :
401
+ ver = 10
402
+ case .v11 :
403
+ ver = 11
404
+ case .v12 :
405
+ ver = 12
406
+ case .v13 :
407
+ ver = 13
408
+ case .v14 :
409
+ ver = 14
410
+ case .v15 :
411
+ ver = 15
412
+ case .v16 :
413
+ ver = 16
414
+ }
415
+ let res = ShutdownScript.newWitnessProgram (version : ver, program : program)
416
+ if res.isOk () {
417
+ return Bindings.Result_ShutdownScriptNoneZ .initWithOk (o : res.getValue ()! )
418
+ }
419
+ }
420
+ return .initWithErr ()
421
+ } catch {
422
+ return .initWithErr ()
423
+ }
424
+ }
425
+
426
+ // ... and redirect all other trait method implementations to the `inner` `KeysManager`.
427
+ override func deriveChannelSigner (channelValueSatoshis : UInt64 , channelKeysId : [UInt8 ]) -> Bindings.WriteableEcdsaChannelSigner {
428
+ return myKeysManager! .inner .asSignerProvider ().deriveChannelSigner (
429
+ channelValueSatoshis : channelValueSatoshis,
430
+ channelKeysId : channelKeysId
431
+ )
432
+ }
433
+
434
+ override func generateChannelKeysId (inbound : Bool , channelValueSatoshis : UInt64 , userChannelId : [UInt8 ]) -> [UInt8 ] {
435
+ return myKeysManager! .inner .asSignerProvider ().generateChannelKeysId (
436
+ inbound : inbound,
437
+ channelValueSatoshis : channelValueSatoshis,
438
+ userChannelId : userChannelId
439
+ )
440
+ }
441
+
442
+ override func readChanSigner (reader : [UInt8 ]) -> Bindings.Result_WriteableEcdsaChannelSignerDecodeErrorZ {
443
+ return myKeysManager! .inner .asSignerProvider ().readChanSigner (reader : reader)
444
+ }
445
+ }
446
+ ```
447
+ </template >
277
448
</CodeSwitcher >
0 commit comments