diff --git a/src/builder.rs b/src/builder.rs index b60123844..edb55cc6f 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -591,12 +591,9 @@ fn build_with_store_internal( // Initialize the ChannelManager let mut user_config = UserConfig::default(); user_config.channel_handshake_limits.force_announced_channel_preference = false; + user_config.manually_accept_inbound_channels = true; + user_config.channel_handshake_config.negotiate_anchors_zero_fee_htlc_tx = true; - if !config.trusted_peers_0conf.is_empty() { - // Manually accept inbound channels if we expect 0conf channel requests, avoid - // generating the events otherwise. - user_config.manually_accept_inbound_channels = true; - } let channel_manager = { if let Ok(res) = kv_store.read( CHANNEL_MANAGER_PERSISTENCE_PRIMARY_NAMESPACE, diff --git a/src/event.rs b/src/event.rs index 56c8f74a2..08f71de2e 100644 --- a/src/event.rs +++ b/src/event.rs @@ -1,6 +1,7 @@ use crate::types::{Sweeper, Wallet}; use crate::{ - hex_utils, ChannelManager, Config, Error, NetworkGraph, PeerInfo, PeerStore, UserChannelId, + hex_utils, BumpTransactionEventHandler, ChannelManager, Config, Error, NetworkGraph, PeerInfo, + PeerStore, UserChannelId, }; use crate::payment_store::{ @@ -246,6 +247,7 @@ where { event_queue: Arc>, wallet: Arc, + bump_tx_event_handler: Arc, channel_manager: Arc>, output_sweeper: Arc>, network_graph: Arc, @@ -263,13 +265,14 @@ where pub fn new( event_queue: Arc>, wallet: Arc, channel_manager: Arc>, output_sweeper: Arc>, - network_graph: Arc, payment_store: Arc>, - peer_store: Arc>, runtime: Arc>>, - logger: L, config: Arc, + bump_tx_event_handler: Arc, network_graph: Arc, + payment_store: Arc>, peer_store: Arc>, + runtime: Arc>>, logger: L, config: Arc, ) -> Self { Self { event_queue, wallet, + bump_tx_event_handler, channel_manager, output_sweeper, network_graph, @@ -785,7 +788,9 @@ where } LdkEvent::DiscardFunding { .. } => {} LdkEvent::HTLCIntercepted { .. } => {} - LdkEvent::BumpTransaction(_) => {} + LdkEvent::BumpTransaction(bte) => { + self.bump_tx_event_handler.handle_event(&bte); + } LdkEvent::InvoiceRequestFailed { .. } => {} LdkEvent::ConnectionNeeded { .. } => {} } diff --git a/src/lib.rs b/src/lib.rs index 2f09f2fd3..aadeed997 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -121,14 +121,15 @@ use payment_store::PaymentStore; pub use payment_store::{PaymentDetails, PaymentDirection, PaymentStatus}; use peer_store::{PeerInfo, PeerStore}; use types::{ - Broadcaster, ChainMonitor, ChannelManager, FeeEstimator, KeysManager, NetworkGraph, - PeerManager, Router, Scorer, Sweeper, Wallet, + Broadcaster, BumpTransactionEventHandler, ChainMonitor, ChannelManager, FeeEstimator, + KeysManager, NetworkGraph, PeerManager, Router, Scorer, Sweeper, Wallet, }; pub use types::{ChannelDetails, Network, PeerDetails, UserChannelId}; use logger::{log_error, log_info, log_trace, FilesystemLogger, Logger}; use lightning::chain::Confirm; +use lightning::events::bump_transaction::Wallet as LdkWallet; use lightning::ln::channelmanager::{self, PaymentId, RecipientOnionFields, Retry}; use lightning::ln::msgs::SocketAddress; use lightning::ln::{ChannelId, PaymentHash, PaymentPreimage}; @@ -693,11 +694,19 @@ impl Node { } }); + let bump_tx_event_handler = Arc::new(BumpTransactionEventHandler::new( + Arc::clone(&self.tx_broadcaster), + Arc::new(LdkWallet::new(Arc::clone(&self.wallet), Arc::clone(&self.logger))), + Arc::clone(&self.keys_manager), + Arc::clone(&self.logger), + )); + let event_handler = Arc::new(EventHandler::new( Arc::clone(&self.event_queue), Arc::clone(&self.wallet), Arc::clone(&self.channel_manager), Arc::clone(&self.output_sweeper), + bump_tx_event_handler, Arc::clone(&self.network_graph), Arc::clone(&self.payment_store), Arc::clone(&self.peer_store), diff --git a/src/types.rs b/src/types.rs index b5926cb99..39165e698 100644 --- a/src/types.rs +++ b/src/types.rs @@ -191,6 +191,14 @@ impl FromStr for Network { } } +pub(crate) type BumpTransactionEventHandler = + lightning::events::bump_transaction::BumpTransactionEventHandler< + Arc, + Arc, Arc>>, + Arc, + Arc, + >; + /// A local, potentially user-provided, identifier of a channel. /// /// By default, this will be randomly generated for the user to ensure local uniqueness. diff --git a/src/wallet.rs b/src/wallet.rs index d3e6c0a47..6e4172d84 100644 --- a/src/wallet.rs +++ b/src/wallet.rs @@ -4,6 +4,7 @@ use crate::Error; use lightning::chain::chaininterface::{BroadcasterInterface, ConfirmationTarget, FeeEstimator}; +use lightning::events::bump_transaction::{Utxo, WalletSource}; use lightning::ln::msgs::{DecodeError, UnsignedGossipMessage}; use lightning::ln::script::ShutdownScript; use lightning::sign::{ @@ -19,8 +20,12 @@ use bdk::wallet::AddressIndex; use bdk::FeeRate; use bdk::{SignOptions, SyncOptions}; +use bitcoin::address::{Payload, WitnessVersion}; use bitcoin::bech32::u5; use bitcoin::blockdata::locktime::absolute::LockTime; +use bitcoin::hash_types::WPubkeyHash; +use bitcoin::hashes::Hash; +use bitcoin::psbt::PartiallySignedTransaction; use bitcoin::secp256k1::ecdh::SharedSecret; use bitcoin::secp256k1::ecdsa::{RecoverableSignature, Signature}; use bitcoin::secp256k1::{PublicKey, Scalar, Secp256k1, Signing}; @@ -245,6 +250,97 @@ where } } +impl WalletSource for Wallet +where + D: BatchDatabase, + B::Target: BroadcasterInterface, + E::Target: FeeEstimator, + L::Target: Logger, +{ + fn list_confirmed_utxos(&self) -> Result, ()> { + let locked_wallet = self.inner.lock().unwrap(); + let mut utxos = Vec::new(); + let txs = locked_wallet.list_transactions(true).map_err(|e| { + log_error!(self.logger, "Failed to retrieve transactions from wallet: {}", e); + })?; + let unspent = locked_wallet.list_unspent().map_err(|e| { + log_error!(self.logger, "Failed to retrieve unspent transactions from wallet: {}", e); + })?; + + for u in unspent { + for t in &txs { + if u.outpoint.txid == t.txid && t.confirmation_time.is_some() { + let payload = Payload::from_script(&u.txout.script_pubkey).map_err(|e| { + log_error!(self.logger, "Failed to retrieve script payload: {}", e); + })?; + + match payload { + Payload::WitnessProgram(program) => { + if program.version() == WitnessVersion::V0 + && program.program().len() == 20 + { + let wpkh = WPubkeyHash::from_slice(program.program().as_bytes()) + .map_err(|e| { + log_error!( + self.logger, + "Failed to retrieve script payload: {}", + e + ); + })?; + let utxo = Utxo::new_v0_p2wpkh(u.outpoint, u.txout.value, &wpkh); + utxos.push(utxo); + } else { + log_error!( + self.logger, + "Unexpected program length: {}", + program.program().len() + ); + } + } + _ => { + log_error!( + self.logger, + "Tried to use a non-witness script. This must never happen." + ); + panic!("Tried to use a non-witness script. This must never happen."); + } + } + } + } + } + + Ok(utxos) + } + + fn get_change_script(&self) -> Result { + let locked_wallet = self.inner.lock().unwrap(); + let address_info = locked_wallet.get_address(AddressIndex::New).map_err(|e| { + log_error!(self.logger, "Failed to retrieve new address from wallet: {}", e); + })?; + + Ok(address_info.address.script_pubkey()) + } + + fn sign_psbt(&self, mut psbt: PartiallySignedTransaction) -> Result { + let locked_wallet = self.inner.lock().unwrap(); + + match locked_wallet.sign(&mut psbt, SignOptions::default()) { + Ok(finalized) => { + if !finalized { + log_error!(self.logger, "Failed to finalize PSBT."); + return Err(()); + } + } + Err(err) => { + log_error!(self.logger, "Failed to sign transaction: {}", err); + return Err(()); + } + } + + Ok(psbt.extract_tx()) + } +} + /// Similar to [`KeysManager`], but overrides the destination and shutdown scripts so they are /// directly spendable by the BDK wallet. pub struct WalletKeysManager @@ -398,11 +494,10 @@ where })?; match address.payload { - bitcoin::address::Payload::WitnessProgram(program) => { - ShutdownScript::new_witness_program(&program).map_err(|e| { + Payload::WitnessProgram(program) => ShutdownScript::new_witness_program(&program) + .map_err(|e| { log_error!(self.logger, "Invalid shutdown script: {:?}", e); - }) - } + }), _ => { log_error!( self.logger,