@@ -4,6 +4,7 @@ use crate::Error;
44
55use lightning:: chain:: chaininterface:: { BroadcasterInterface , ConfirmationTarget , FeeEstimator } ;
66
7+ use lightning:: events:: bump_transaction:: { Utxo , WalletSource } ;
78use lightning:: ln:: msgs:: { DecodeError , UnsignedGossipMessage } ;
89use lightning:: ln:: script:: ShutdownScript ;
910use lightning:: sign:: {
@@ -19,8 +20,14 @@ use bdk::wallet::AddressIndex;
1920use bdk:: FeeRate ;
2021use bdk:: { SignOptions , SyncOptions } ;
2122
23+ use bitcoin:: address:: { Payload , WitnessVersion } ;
2224use bitcoin:: bech32:: u5;
25+ use bitcoin:: blockdata:: constants:: WITNESS_SCALE_FACTOR ;
2326use 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 ;
2431use bitcoin:: secp256k1:: ecdh:: SharedSecret ;
2532use bitcoin:: secp256k1:: ecdsa:: { RecoverableSignature , Signature } ;
2633use bitcoin:: secp256k1:: { PublicKey , Scalar , Secp256k1 , Signing } ;
@@ -245,6 +252,118 @@ where
245252 }
246253}
247254
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+
248367/// Similar to [`KeysManager`], but overrides the destination and shutdown scripts so they are
249368/// directly spendable by the BDK wallet.
250369pub struct WalletKeysManager < D , B : Deref , E : Deref , L : Deref >
@@ -398,11 +517,10 @@ where
398517 } ) ?;
399518
400519 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| {
403522 log_error ! ( self . logger, "Invalid shutdown script: {:?}" , e) ;
404- } )
405- }
523+ } ) ,
406524 _ => {
407525 log_error ! (
408526 self . logger,
0 commit comments