@@ -6,7 +6,7 @@ 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', swift:'Swift'} " >
9
+ <CodeSwitcher :languages =" {rust:'Rust', kotlin:'Kotlin', swift:'Swift'} " >
10
10
<template v-slot:rust >
11
11
12
12
``` rust
@@ -18,19 +18,6 @@ let keys_interface_impl = lightning::sign::KeysManager::new(&random_32_bytes, st
18
18
19
19
</template >
20
20
21
- <template v-slot:java >
22
-
23
- ``` java
24
- // Fill in key_seed with secure random data, or, on restart, reload the seed from disk.
25
- byte [] key_seed = new byte [32 ];
26
- KeysManager keys_manager = KeysManager . of(key_seed,
27
- System . currentTimeMillis() / 1000 ,
28
- (int ) (System . currentTimeMillis() * 1000 )
29
- );
30
- ```
31
-
32
- </template >
33
-
34
21
<template v-slot:kotlin >
35
22
36
23
``` kotlin
@@ -52,8 +39,8 @@ let seed = [UInt8](repeating: 0, count: 32)
52
39
let timestampSeconds = UInt64 (NSDate ().timeIntervalSince1970 )
53
40
let timestampNanos = UInt32 (truncating : NSNumber (value : timestampSeconds * 1000 * 1000 ))
54
41
self .myKeysManager = KeysManager (
55
- seed : seed,
56
- startingTimeSecs : timestampSeconds,
42
+ seed : seed,
43
+ startingTimeSecs : timestampSeconds,
57
44
startingTimeNanos : timestampNanos
58
45
)
59
46
```
@@ -73,7 +60,7 @@ Using a [BDK](https://bitcoindevkit.org/)-based wallet the steps would be as fol
73
60
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.
74
61
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 ) ).
75
62
76
- <CodeSwitcher :languages =" {rust:'Rust', java:'Java', kotlin:'Kotlin', swift:'Swift'} " >
63
+ <CodeSwitcher :languages =" {rust:'Rust', kotlin:'Kotlin', swift:'Swift'} " >
77
64
<template v-slot:rust >
78
65
79
66
``` rust
@@ -96,36 +83,6 @@ let keys_manager = KeysManager::new(&ldk_seed, cur.as_secs(), cur.subsec_nanos()
96
83
97
84
</template >
98
85
99
- <template v-slot:java >
100
-
101
- ``` java
102
- // Use BDK to create and build the HD wallet
103
- Mnemonic mnemonic = Mnemonic . Companion . fromString(" sock lyrics " +
104
- " village put galaxy " +
105
- " famous pass act ship second diagram pull" );
106
-
107
- // Other supported networks include mainnet (Bitcoin), Regtest, Signet
108
- DescriptorSecretKey bip32RootKey = new DescriptorSecretKey (Network . TESTNET , mnemonic, null );
109
-
110
- DerivationPath ldkDerivationPath = new DerivationPath (" m/535h" );
111
- DescriptorSecretKey ldkChild = bip32RootKey. derive(ldkDerivationPath);
112
-
113
- ByteArrayOutputStream bos = new ByteArrayOutputStream ();
114
- ObjectOutputStream oos = new ObjectOutputStream (bos);
115
- oos. writeObject(ldkChild. secretBytes());
116
- byte [] entropy = bos. toByteArray();
117
-
118
- // Seed the LDK KeysManager with the private key at m/535h
119
- var startupTime = System . currentTimeMillis();
120
- KeysManager keysManager = KeysManager . of(
121
- entropy,
122
- startupTime / 1000 ,
123
- (int ) (startupTime * 1000 )
124
- );
125
- ```
126
-
127
- </template >
128
-
129
86
<template v-slot:kotlin >
130
87
131
88
``` kotlin
@@ -164,8 +121,8 @@ let timestampNanos = UInt32(truncating: NSNumber(value: timestampSeconds * 1000
164
121
165
122
// Seed the LDK KeysManager with the private key at m/535h
166
123
let keysManager = KeysManager (
167
- seed : ldkSeed,
168
- startingTimeSecs : timestampSeconds,
124
+ seed : ldkSeed,
125
+ startingTimeSecs : timestampSeconds,
169
126
startingTimeNanos : timestampNanos
170
127
)
171
128
```
@@ -192,7 +149,7 @@ In order to make the outputs from channel closing spendable by a third-party wal
192
149
193
150
For example, a wrapper based on BDK's [ ` Wallet ` ] ( https://docs.rs/bdk/*/bdk/wallet/struct.Wallet.html ) could look like this:
194
151
195
- <CodeSwitcher :languages =" {rust:'Rust', swift:'Swift'} " >
152
+ <CodeSwitcher :languages =" {rust:'Rust', kotlin: 'Kotlin', swift:'Swift'} " >
196
153
<template v-slot:rust >
197
154
198
155
``` rust
@@ -312,6 +269,96 @@ where
312
269
// ... snip
313
270
}
314
271
272
+ ```
273
+
274
+ </template >
275
+
276
+ <template v-slot:kotlin >
277
+
278
+ ``` kotlin
279
+ class LDKKeysManager (seed : ByteArray , startTimeSecs : Long , startTimeNano : Int , wallet : Wallet ) {
280
+ var inner: KeysManager
281
+ var wallet: Wallet
282
+ var signerProvider: LDKSignerProvider
283
+
284
+ init {
285
+ this .inner = KeysManager .of(seed, startTimeSecs, startTimeNano)
286
+ this .wallet = wallet
287
+ signerProvider = LDKSignerProvider ()
288
+ signerProvider.ldkkeysManager = this
289
+ }
290
+
291
+ // We drop all occurences of `SpendableOutputDescriptor::StaticOutput` (since they will be
292
+ // spendable by the BDK wallet) and forward any other descriptors to
293
+ // `KeysManager::spend_spendable_outputs`.
294
+ //
295
+ // Note you should set `locktime` to the current block height to mitigate fee sniping.
296
+ // See https://bitcoinops.org/en/topics/fee-sniping/ for more information.
297
+ fun spend_spendable_outputs (
298
+ descriptors : Array <SpendableOutputDescriptor >,
299
+ outputs : Array <TxOut >,
300
+ changeDestinationScript : ByteArray ,
301
+ feerateSatPer1000Weight : Int ,
302
+ locktime : Option_u32Z
303
+ ): Result_TransactionNoneZ {
304
+ val onlyNonStatic: Array <SpendableOutputDescriptor > = descriptors.filter { it !is SpendableOutputDescriptor .StaticOutput }.toTypedArray()
305
+
306
+ return inner.spend_spendable_outputs(
307
+ onlyNonStatic,
308
+ outputs,
309
+ changeDestinationScript,
310
+ feerateSatPer1000Weight,
311
+ locktime,
312
+ )
313
+ }
314
+ }
315
+
316
+ class LDKSignerProvider : SignerProvider .SignerProviderInterface {
317
+ var ldkkeysManager: LDKKeysManager ? = null
318
+
319
+ override fun generate_channel_keys_id (p0 : Boolean , p1 : Long , p2 : UInt128 ? ): ByteArray {
320
+ return ldkkeysManager!! .inner.as_SignerProvider().generate_channel_keys_id(p0, p1, p2)
321
+ }
322
+
323
+ override fun derive_channel_signer (p0 : Long , p1 : ByteArray? ): WriteableEcdsaChannelSigner {
324
+ return ldkkeysManager!! .inner.as_SignerProvider().derive_channel_signer(p0, p1)
325
+ }
326
+
327
+ override fun read_chan_signer (p0 : ByteArray? ): Result_WriteableEcdsaChannelSignerDecodeErrorZ {
328
+ return ldkkeysManager!! .inner.as_SignerProvider().read_chan_signer(p0)
329
+ }
330
+
331
+ // We return the destination and shutdown scripts derived by the BDK wallet.
332
+ @OptIn(ExperimentalUnsignedTypes ::class )
333
+ override fun get_destination_script (): Result_CVec_u8ZNoneZ {
334
+ val address = ldkkeysManager!! .wallet.getAddress(AddressIndex .New )
335
+ return Result_CVec_u8ZNoneZ .ok(address.address.scriptPubkey().toBytes().toUByteArray().toByteArray())
336
+ }
337
+
338
+ // Only applies to cooperative close transactions.
339
+ override fun get_shutdown_scriptpubkey (): Result_ShutdownScriptNoneZ {
340
+ val address = ldkkeysManager!! .wallet.getAddress(AddressIndex .New ).address
341
+
342
+ return when (val payload: Payload = address.payload()) {
343
+ is Payload .WitnessProgram -> {
344
+ val ver = when (payload.version.name) {
345
+ in " V0" .. " V16" -> payload.version.name.substring(1 ).toIntOrNull() ? : 0
346
+ else -> 0 // Default to 0 if it doesn't match any "V0" to "V16"
347
+ }
348
+
349
+ val result = ShutdownScript .new_witness_program(
350
+ WitnessVersion (ver.toByte()),
351
+ payload.program.toUByteArray().toByteArray()
352
+ )
353
+ Result_ShutdownScriptNoneZ .ok((result as Result_ShutdownScriptInvalidShutdownScriptZ .Result_ShutdownScriptInvalidShutdownScriptZ_OK ).res)
354
+ }
355
+ else -> {
356
+ Result_ShutdownScriptNoneZ .err()
357
+ }
358
+ }
359
+ }
360
+ }
361
+
315
362
```
316
363
317
364
</template >
@@ -323,7 +370,7 @@ class MyKeysManager {
323
370
let inner: KeysManager
324
371
let wallet: BitcoinDevKit.Wallet
325
372
let signerProvider: MySignerProvider
326
-
373
+
327
374
init (seed : [UInt8 ], startingTimeSecs : UInt64 , startingTimeNanos : UInt32 , wallet : BitcoinDevKit.Wallet) {
328
375
self .inner = KeysManager (seed : seed, startingTimeSecs : startingTimeSecs, startingTimeNanos : startingTimeNanos)
329
376
self .wallet = wallet
@@ -359,7 +406,7 @@ class MyKeysManager {
359
406
360
407
class MySignerProvider : SignerProvider {
361
408
weak var myKeysManager: MyKeysManager?
362
-
409
+
363
410
// We return the destination and shutdown scripts derived by the BDK wallet.
364
411
override func getDestinationScript () -> Bindings.Result_ScriptNoneZ {
365
412
do {
@@ -369,7 +416,7 @@ class MySignerProvider: SignerProvider {
369
416
return .initWithErr ()
370
417
}
371
418
}
372
-
419
+
373
420
override func getShutdownScriptpubkey () -> Bindings.Result_ShutdownScriptNoneZ {
374
421
do {
375
422
let address = try myKeysManager! .wallet .getAddress (addressIndex : .new ).address
@@ -422,27 +469,28 @@ class MySignerProvider: SignerProvider {
422
469
return .initWithErr ()
423
470
}
424
471
}
425
-
472
+
426
473
// ... and redirect all other trait method implementations to the `inner` `KeysManager`.
427
474
override func deriveChannelSigner (channelValueSatoshis : UInt64 , channelKeysId : [UInt8 ]) -> Bindings.WriteableEcdsaChannelSigner {
428
475
return myKeysManager! .inner .asSignerProvider ().deriveChannelSigner (
429
476
channelValueSatoshis : channelValueSatoshis,
430
477
channelKeysId : channelKeysId
431
478
)
432
479
}
433
-
480
+
434
481
override func generateChannelKeysId (inbound : Bool , channelValueSatoshis : UInt64 , userChannelId : [UInt8 ]) -> [UInt8 ] {
435
482
return myKeysManager! .inner .asSignerProvider ().generateChannelKeysId (
436
483
inbound : inbound,
437
484
channelValueSatoshis : channelValueSatoshis,
438
485
userChannelId : userChannelId
439
486
)
440
487
}
441
-
488
+
442
489
override func readChanSigner (reader : [UInt8 ]) -> Bindings.Result_WriteableEcdsaChannelSignerDecodeErrorZ {
443
490
return myKeysManager! .inner .asSignerProvider ().readChanSigner (reader : reader)
444
491
}
445
492
}
446
493
```
494
+
447
495
</template >
448
496
</CodeSwitcher >
0 commit comments