diff --git a/src/builder.rs b/src/builder.rs index c7d6b2d17..bc2b082c6 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -548,12 +548,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(mut reader) = kv_store.read(CHANNEL_MANAGER_PERSISTENCE_NAMESPACE, CHANNEL_MANAGER_PERSISTENCE_KEY) diff --git a/src/event.rs b/src/event.rs index 31a115da5..4853cbb4e 100644 --- a/src/event.rs +++ b/src/event.rs @@ -1,6 +1,6 @@ use crate::{ - hex_utils, ChannelId, ChannelManager, Config, Error, KeysManager, NetworkGraph, UserChannelId, - Wallet, + hex_utils, BumpTransactionEventHandler, ChannelId, ChannelManager, Config, Error, KeysManager, + NetworkGraph, UserChannelId, Wallet, }; use crate::payment_store::{ @@ -223,8 +223,9 @@ pub(crate) struct EventHandler where L::Target: Logger, { - wallet: Arc>, event_queue: Arc>, + wallet: Arc>, + bump_tx_event_handler: Arc, channel_manager: Arc>, network_graph: Arc, keys_manager: Arc, @@ -239,7 +240,8 @@ where L::Target: Logger, { pub fn new( - wallet: Arc>, event_queue: Arc>, + event_queue: Arc>, wallet: Arc>, + bump_tx_event_handler: Arc, channel_manager: Arc>, network_graph: Arc, keys_manager: Arc, payment_store: Arc>, runtime: Arc>>, logger: L, config: Arc, @@ -247,6 +249,7 @@ where Self { event_queue, wallet, + bump_tx_event_handler, channel_manager, network_graph, keys_manager, @@ -760,7 +763,9 @@ where } LdkEvent::DiscardFunding { .. } => {} LdkEvent::HTLCIntercepted { .. } => {} - LdkEvent::BumpTransaction(_) => {} + LdkEvent::BumpTransaction(bte) => { + self.bump_tx_event_handler.handle_event(&bte); + } } } } diff --git a/src/lib.rs b/src/lib.rs index 704c2bb66..971a61c25 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -118,13 +118,17 @@ use io::KVStore; use payment_store::PaymentStore; pub use payment_store::{PaymentDetails, PaymentDirection, PaymentStatus}; use peer_store::{PeerInfo, PeerStore}; -use types::{ChainMonitor, ChannelManager, KeysManager, NetworkGraph, PeerManager, Scorer}; +use types::{ + BumpTransactionEventHandler, ChainMonitor, ChannelManager, KeysManager, NetworkGraph, + PeerManager, Scorer, +}; pub use types::{ChannelDetails, ChannelId, PeerDetails, UserChannelId}; use wallet::Wallet; 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::{PaymentHash, PaymentPreimage}; use lightning::sign::EntropySource; @@ -634,9 +638,17 @@ impl Node { } }); - let event_handler = Arc::new(EventHandler::new( + let bump_tx_event_handler = Arc::new(BumpTransactionEventHandler::new( Arc::clone(&self.wallet), + Arc::new(LdkWallet::new(Arc::clone(&self.wallet))), + 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), + bump_tx_event_handler, Arc::clone(&self.channel_manager), Arc::clone(&self.network_graph), Arc::clone(&self.keys_manager), diff --git a/src/types.rs b/src/types.rs index 298c9421e..90444018d 100644 --- a/src/types.rs +++ b/src/types.rs @@ -105,6 +105,18 @@ impl lightning::onion_message::MessageRouter for FakeMessageRouter { } } +pub(crate) type BumpTransactionEventHandler = + lightning::events::bump_transaction::BumpTransactionEventHandler< + Arc>>, + Arc< + lightning::events::bump_transaction::Wallet< + Arc>>, + >, + >, + Arc>>, + Arc, + >; + /// The global identifier of a channel. /// /// Note that this will start out to be a temporary ID until channel funding negotiation is diff --git a/src/wallet.rs b/src/wallet.rs index cc08ff198..d9c007523 100644 --- a/src/wallet.rs +++ b/src/wallet.rs @@ -6,6 +6,7 @@ use lightning::chain::chaininterface::{ BroadcasterInterface, ConfirmationTarget, FeeEstimator, FEERATE_FLOOR_SATS_PER_KW, }; +use lightning::events::bump_transaction::{Utxo, WalletSource}; use lightning::ln::msgs::{DecodeError, UnsignedGossipMessage}; use lightning::ln::script::ShutdownScript; use lightning::sign::{ @@ -21,10 +22,13 @@ use bdk::wallet::AddressIndex; use bdk::{FeeRate, SignOptions, SyncOptions}; use bitcoin::bech32::u5; +use bitcoin::hashes::Hash; use bitcoin::secp256k1::ecdh::SharedSecret; use bitcoin::secp256k1::ecdsa::{RecoverableSignature, Signature}; use bitcoin::secp256k1::{PublicKey, Scalar, Secp256k1, Signing}; -use bitcoin::{LockTime, PackedLockTime, Script, Transaction, TxOut, Txid}; +use bitcoin::util::address::WitnessVersion; +use bitcoin::util::psbt::PartiallySignedTransaction; +use bitcoin::{LockTime, PackedLockTime, Script, Transaction, TxOut, Txid, WPubkeyHash}; use std::collections::HashMap; use std::ops::Deref; @@ -347,6 +351,100 @@ where } } +impl WalletSource for Wallet +where + D: BatchDatabase, + 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 = + bitcoin::util::address::Payload::from_script(&u.txout.script_pubkey) + .map_err(|e| { + log_error!(self.logger, "Failed to retrieve script payload: {}", e); + })?; + + match payload { + bitcoin::util::address::Payload::WitnessProgram { version, program } => { + if version == WitnessVersion::V0 && program.len() == 20 { + let wpkh = WPubkeyHash::from_slice(&program).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.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_tx(&self, tx: &mut Transaction) -> Result<(), ()> { + let locked_wallet = self.inner.lock().unwrap(); + + let mut psbt = PartiallySignedTransaction::from_unsigned_tx(tx.clone()).map_err(|e| { + log_error!(self.logger, "Failed to create PSBT: {}", e); + })?; + + 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(()); + } + } + + *tx = psbt.extract_tx(); + + Ok(()) + } +} + /// Similar to [`KeysManager`], but overrides the destination and shutdown scripts so they are /// directly spendable by the BDK wallet. pub struct WalletKeysManager