@@ -4,6 +4,7 @@ use crate::Error;
4
4
5
5
use lightning:: chain:: chaininterface:: { BroadcasterInterface , ConfirmationTarget , FeeEstimator } ;
6
6
7
+ use lightning:: events:: bump_transaction:: { Utxo , WalletSource } ;
7
8
use lightning:: ln:: msgs:: { DecodeError , UnsignedGossipMessage } ;
8
9
use lightning:: ln:: script:: ShutdownScript ;
9
10
use lightning:: sign:: {
@@ -19,8 +20,14 @@ use bdk::wallet::AddressIndex;
19
20
use bdk:: FeeRate ;
20
21
use bdk:: { SignOptions , SyncOptions } ;
21
22
23
+ use bitcoin:: address:: { Payload , WitnessVersion } ;
22
24
use bitcoin:: bech32:: u5;
25
+ use bitcoin:: blockdata:: constants:: WITNESS_SCALE_FACTOR ;
23
26
use bitcoin:: blockdata:: locktime:: absolute:: LockTime ;
27
+ use bitcoin:: hash_types:: WPubkeyHash ;
28
+ use bitcoin:: hashes:: Hash ;
29
+ use bitcoin:: key:: XOnlyPublicKey ;
30
+ use bitcoin:: psbt:: PartiallySignedTransaction ;
24
31
use bitcoin:: secp256k1:: ecdh:: SharedSecret ;
25
32
use bitcoin:: secp256k1:: ecdsa:: { RecoverableSignature , Signature } ;
26
33
use bitcoin:: secp256k1:: { PublicKey , Scalar , Secp256k1 , Signing } ;
@@ -245,6 +252,118 @@ where
245
252
}
246
253
}
247
254
255
+ impl < D , B : Deref , E : Deref , L : Deref > WalletSource for Wallet < D , B , E , L >
256
+ where
257
+ D : BatchDatabase ,
258
+ B :: Target : BroadcasterInterface ,
259
+ E :: Target : FeeEstimator ,
260
+ L :: Target : Logger ,
261
+ {
262
+ fn list_confirmed_utxos ( & self ) -> Result < Vec < Utxo > , ( ) > {
263
+ let locked_wallet = self . inner . lock ( ) . unwrap ( ) ;
264
+ let mut utxos = Vec :: new ( ) ;
265
+ let confirmed_txs: Vec < bdk:: TransactionDetails > = locked_wallet
266
+ . list_transactions ( false )
267
+ . map_err ( |e| {
268
+ log_error ! ( self . logger, "Failed to retrieve transactions from wallet: {}" , e) ;
269
+ } ) ?
270
+ . into_iter ( )
271
+ . filter ( |t| t. confirmation_time . is_some ( ) )
272
+ . collect ( ) ;
273
+ let unspent_confirmed_utxos = locked_wallet
274
+ . list_unspent ( )
275
+ . map_err ( |e| {
276
+ log_error ! (
277
+ self . logger,
278
+ "Failed to retrieve unspent transactions from wallet: {}" ,
279
+ e
280
+ ) ;
281
+ } ) ?
282
+ . into_iter ( )
283
+ . filter ( |u| confirmed_txs. iter ( ) . find ( |t| t. txid == u. outpoint . txid ) . is_some ( ) ) ;
284
+
285
+ for u in unspent_confirmed_utxos {
286
+ let payload = Payload :: from_script ( & u. txout . script_pubkey ) . map_err ( |e| {
287
+ log_error ! ( self . logger, "Failed to retrieve script payload: {}" , e) ;
288
+ } ) ?;
289
+
290
+ match payload {
291
+ Payload :: WitnessProgram ( program) => match program. version ( ) {
292
+ WitnessVersion :: V0 if program. program ( ) . len ( ) == 20 => {
293
+ let wpkh =
294
+ WPubkeyHash :: from_slice ( program. program ( ) . as_bytes ( ) ) . map_err ( |e| {
295
+ log_error ! ( self . logger, "Failed to retrieve script payload: {}" , e) ;
296
+ } ) ?;
297
+ let utxo = Utxo :: new_v0_p2wpkh ( u. outpoint , u. txout . value , & wpkh) ;
298
+ utxos. push ( utxo) ;
299
+ }
300
+ WitnessVersion :: V1 => {
301
+ XOnlyPublicKey :: from_slice ( program. program ( ) . as_bytes ( ) ) . map_err ( |e| {
302
+ log_error ! ( self . logger, "Failed to retrieve script payload: {}" , e) ;
303
+ } ) ?;
304
+
305
+ let utxo = Utxo {
306
+ outpoint : u. outpoint ,
307
+ output : TxOut {
308
+ value : u. txout . value ,
309
+ script_pubkey : ScriptBuf :: new_witness_program ( & program) ,
310
+ } ,
311
+ satisfaction_weight : 1 /* empty script_sig */ * WITNESS_SCALE_FACTOR as u64 +
312
+ 1 /* witness items */ + 1 /* schnorr sig len */ + 64 , /* schnorr sig */
313
+ } ;
314
+ utxos. push ( utxo) ;
315
+ }
316
+ _ => {
317
+ log_error ! (
318
+ self . logger,
319
+ "Unexpected witness version or length. Version: {}, Length: {}" ,
320
+ program. version( ) ,
321
+ program. program( ) . len( )
322
+ ) ;
323
+ }
324
+ } ,
325
+ _ => {
326
+ log_error ! (
327
+ self . logger,
328
+ "Tried to use a non-witness script. This must never happen."
329
+ ) ;
330
+ panic ! ( "Tried to use a non-witness script. This must never happen." ) ;
331
+ }
332
+ }
333
+ }
334
+
335
+ Ok ( utxos)
336
+ }
337
+
338
+ fn get_change_script ( & self ) -> Result < ScriptBuf , ( ) > {
339
+ let locked_wallet = self . inner . lock ( ) . unwrap ( ) ;
340
+ let address_info = locked_wallet. get_address ( AddressIndex :: New ) . map_err ( |e| {
341
+ log_error ! ( self . logger, "Failed to retrieve new address from wallet: {}" , e) ;
342
+ } ) ?;
343
+
344
+ Ok ( address_info. address . script_pubkey ( ) )
345
+ }
346
+
347
+ fn sign_psbt ( & self , mut psbt : PartiallySignedTransaction ) -> Result < Transaction , ( ) > {
348
+ let locked_wallet = self . inner . lock ( ) . unwrap ( ) ;
349
+
350
+ match locked_wallet. sign ( & mut psbt, SignOptions :: default ( ) ) {
351
+ Ok ( finalized) => {
352
+ if !finalized {
353
+ log_error ! ( self . logger, "Failed to finalize PSBT." ) ;
354
+ return Err ( ( ) ) ;
355
+ }
356
+ }
357
+ Err ( err) => {
358
+ log_error ! ( self . logger, "Failed to sign transaction: {}" , err) ;
359
+ return Err ( ( ) ) ;
360
+ }
361
+ }
362
+
363
+ Ok ( psbt. extract_tx ( ) )
364
+ }
365
+ }
366
+
248
367
/// Similar to [`KeysManager`], but overrides the destination and shutdown scripts so they are
249
368
/// directly spendable by the BDK wallet.
250
369
pub struct WalletKeysManager < D , B : Deref , E : Deref , L : Deref >
@@ -398,11 +517,10 @@ where
398
517
} ) ?;
399
518
400
519
match address. payload {
401
- bitcoin :: address :: Payload :: WitnessProgram ( program) => {
402
- ShutdownScript :: new_witness_program ( & program ) . map_err ( |e| {
520
+ Payload :: WitnessProgram ( program) => ShutdownScript :: new_witness_program ( & program )
521
+ . map_err ( |e| {
403
522
log_error ! ( self . logger, "Invalid shutdown script: {:?}" , e) ;
404
- } )
405
- }
523
+ } ) ,
406
524
_ => {
407
525
log_error ! (
408
526
self . logger,
0 commit comments