diff --git a/Cargo.lock b/Cargo.lock index ca693af3d..b37d37f3c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -257,9 +257,9 @@ checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" [[package]] name = "bdk" -version = "0.27.1" +version = "0.28.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51c878ac60a45c41523ff790df555ccb1fbfd634a667220104dcae3e64d7ed9f" +checksum = "b15adb2017ab6437b6704a779ab8bbefe857612f5af9d84b677a1767f965e099" dependencies = [ "async-trait", "bdk-macros", @@ -998,7 +998,7 @@ dependencies = [ [[package]] name = "dlc" version = "0.4.0" -source = "git+https://github.com/p2pderivatives/rust-dlc?rev=9815582#98155820789a4a44a93a5351c0f99db9335447b9" +source = "git+https://github.com/p2pderivatives/rust-dlc?rev=d1e08a0#d1e08a0e8269c3dfb75d673beebc0fac87d58236" dependencies = [ "bitcoin", "miniscript 8.0.0", @@ -1010,7 +1010,7 @@ dependencies = [ [[package]] name = "dlc-manager" version = "0.4.0" -source = "git+https://github.com/p2pderivatives/rust-dlc?rev=9815582#98155820789a4a44a93a5351c0f99db9335447b9" +source = "git+https://github.com/p2pderivatives/rust-dlc?rev=d1e08a0#d1e08a0e8269c3dfb75d673beebc0fac87d58236" dependencies = [ "async-trait", "bitcoin", @@ -1026,7 +1026,7 @@ dependencies = [ [[package]] name = "dlc-messages" version = "0.4.0" -source = "git+https://github.com/p2pderivatives/rust-dlc?rev=9815582#98155820789a4a44a93a5351c0f99db9335447b9" +source = "git+https://github.com/p2pderivatives/rust-dlc?rev=d1e08a0#d1e08a0e8269c3dfb75d673beebc0fac87d58236" dependencies = [ "bitcoin", "dlc", @@ -1039,7 +1039,7 @@ dependencies = [ [[package]] name = "dlc-sled-storage-provider" version = "0.1.0" -source = "git+https://github.com/p2pderivatives/rust-dlc?rev=9815582#98155820789a4a44a93a5351c0f99db9335447b9" +source = "git+https://github.com/p2pderivatives/rust-dlc?rev=d1e08a0#d1e08a0e8269c3dfb75d673beebc0fac87d58236" dependencies = [ "bitcoin", "dlc-manager", @@ -1053,7 +1053,7 @@ dependencies = [ [[package]] name = "dlc-trie" version = "0.4.0" -source = "git+https://github.com/p2pderivatives/rust-dlc?rev=9815582#98155820789a4a44a93a5351c0f99db9335447b9" +source = "git+https://github.com/p2pderivatives/rust-dlc?rev=d1e08a0#d1e08a0e8269c3dfb75d673beebc0fac87d58236" dependencies = [ "bitcoin", "dlc", @@ -1131,9 +1131,9 @@ dependencies = [ [[package]] name = "esplora-client" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38bbba572d03ca4d628b653f01e60ba6947c67df65ee8910c79daaf252897924" +checksum = "847e59bd6ee1c3f2bdf217118ee3640b97a1b1d8becb55771e67e533b87da66f" dependencies = [ "bitcoin", "log", @@ -1746,30 +1746,31 @@ dependencies = [ [[package]] name = "lightning" -version = "0.0.114" -source = "git+https://github.com/p2pderivatives/rust-lightning/?rev=698be7fb#698be7fb822907e32afbde5144e70f6c9fe48f26" +version = "0.0.116" +source = "git+https://github.com/p2pderivatives/rust-lightning/?rev=a57281b#a57281b2d915c7a752ed12ed380a9989b03faa66" dependencies = [ "bitcoin", + "musig2", ] [[package]] name = "lightning-background-processor" -version = "0.0.114" -source = "git+https://github.com/p2pderivatives/rust-lightning/?rev=698be7fb#698be7fb822907e32afbde5144e70f6c9fe48f26" +version = "0.0.116" +source = "git+https://github.com/p2pderivatives/rust-lightning/?rev=a57281b#a57281b2d915c7a752ed12ed380a9989b03faa66" dependencies = [ "bitcoin", - "futures-util", "lightning", "lightning-rapid-gossip-sync", ] [[package]] name = "lightning-invoice" -version = "0.22.0" +version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adfb59c6d13e130aece30fc72a7c17d74b201aed0ffb201b740f36e07aaece32" +checksum = "1788c0158526ec27a502043c2911ea6ea58fdc656bdf8749484942c07b790d23" dependencies = [ "bech32", + "bitcoin", "bitcoin_hashes", "lightning", "num-traits", @@ -1778,8 +1779,8 @@ dependencies = [ [[package]] name = "lightning-net-tokio" -version = "0.0.114" -source = "git+https://github.com/p2pderivatives/rust-lightning/?rev=698be7fb#698be7fb822907e32afbde5144e70f6c9fe48f26" +version = "0.0.116" +source = "git+https://github.com/p2pderivatives/rust-lightning/?rev=a57281b#a57281b2d915c7a752ed12ed380a9989b03faa66" dependencies = [ "bitcoin", "lightning", @@ -1788,8 +1789,8 @@ dependencies = [ [[package]] name = "lightning-persister" -version = "0.0.114" -source = "git+https://github.com/p2pderivatives/rust-lightning/?rev=698be7fb#698be7fb822907e32afbde5144e70f6c9fe48f26" +version = "0.0.116" +source = "git+https://github.com/p2pderivatives/rust-lightning/?rev=a57281b#a57281b2d915c7a752ed12ed380a9989b03faa66" dependencies = [ "bitcoin", "libc", @@ -1799,8 +1800,8 @@ dependencies = [ [[package]] name = "lightning-rapid-gossip-sync" -version = "0.0.114" -source = "git+https://github.com/p2pderivatives/rust-lightning/?rev=698be7fb#698be7fb822907e32afbde5144e70f6c9fe48f26" +version = "0.0.116" +source = "git+https://github.com/p2pderivatives/rust-lightning/?rev=a57281b#a57281b2d915c7a752ed12ed380a9989b03faa66" dependencies = [ "bitcoin", "lightning", @@ -1808,8 +1809,8 @@ dependencies = [ [[package]] name = "lightning-transaction-sync" -version = "0.0.114" -source = "git+https://github.com/p2pderivatives/rust-lightning/?rev=698be7fb#698be7fb822907e32afbde5144e70f6c9fe48f26" +version = "0.0.116" +source = "git+https://github.com/p2pderivatives/rust-lightning/?rev=a57281b#a57281b2d915c7a752ed12ed380a9989b03faa66" dependencies = [ "bdk-macros", "bitcoin", @@ -2146,6 +2147,14 @@ dependencies = [ "windows-sys 0.45.0", ] +[[package]] +name = "musig2" +version = "0.1.0" +source = "git+https://github.com/arik-so/rust-musig2?rev=27797d7#27797d78cf64e8974e38d7f31ebb11e455015a9e" +dependencies = [ + "bitcoin", +] + [[package]] name = "native" version = "0.1.0" @@ -2465,7 +2474,7 @@ checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" [[package]] name = "p2pd-oracle-client" version = "0.1.0" -source = "git+https://github.com/p2pderivatives/rust-dlc?rev=9815582#98155820789a4a44a93a5351c0f99db9335447b9" +source = "git+https://github.com/p2pderivatives/rust-dlc?rev=d1e08a0#d1e08a0e8269c3dfb75d673beebc0fac87d58236" dependencies = [ "chrono", "dlc-manager", @@ -3330,7 +3339,7 @@ dependencies = [ [[package]] name = "simple-wallet" version = "0.1.0" -source = "git+https://github.com/p2pderivatives/rust-dlc?rev=9815582#98155820789a4a44a93a5351c0f99db9335447b9" +source = "git+https://github.com/p2pderivatives/rust-dlc?rev=d1e08a0#d1e08a0e8269c3dfb75d673beebc0fac87d58236" dependencies = [ "bitcoin", "dlc", diff --git a/Cargo.toml b/Cargo.toml index b62095ddf..2ba95fb03 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,21 +17,20 @@ resolver = "2" [patch.crates-io] # We should usually track the `p2pderivatives/feature/ln-dlc-channels[-10101]` branch -dlc-manager = { git = "https://github.com/p2pderivatives/rust-dlc", rev = "9815582" } -dlc-messages = { git = "https://github.com/p2pderivatives/rust-dlc", rev = "9815582" } -dlc = { git = "https://github.com/p2pderivatives/rust-dlc", rev = "9815582" } -dlc-sled-storage-provider = { git = "https://github.com/p2pderivatives/rust-dlc", rev = "9815582" } -p2pd-oracle-client = { git = "https://github.com/p2pderivatives/rust-dlc", rev = "9815582" } -dlc-trie = { git = "https://github.com/p2pderivatives/rust-dlc", rev = "9815582" } -simple-wallet = { git = "https://github.com/p2pderivatives/rust-dlc", rev = "9815582" } +dlc-manager = { git = "https://github.com/p2pderivatives/rust-dlc", rev = "d1e08a0" } +dlc-messages = { git = "https://github.com/p2pderivatives/rust-dlc", rev = "d1e08a0" } +dlc = { git = "https://github.com/p2pderivatives/rust-dlc", rev = "d1e08a0" } +dlc-sled-storage-provider = { git = "https://github.com/p2pderivatives/rust-dlc", rev = "d1e08a0" } +p2pd-oracle-client = { git = "https://github.com/p2pderivatives/rust-dlc", rev = "d1e08a0" } +dlc-trie = { git = "https://github.com/p2pderivatives/rust-dlc", rev = "d1e08a0" } +simple-wallet = { git = "https://github.com/p2pderivatives/rust-dlc", rev = "d1e08a0" } # We should usually track the `p2pderivatives/split-tx-experiment[-10101]` branch -# Temporary fix: this fork allows us to access the channel monitor's channel_key_ids -lightning = { git = "https://github.com/p2pderivatives/rust-lightning/", rev = "698be7fb" } -lightning-background-processor = { git = "https://github.com/p2pderivatives/rust-lightning/", rev = "698be7fb" } -lightning-transaction-sync = { git = "https://github.com/p2pderivatives/rust-lightning/", rev = "698be7fb" } -lightning-net-tokio = { git = "https://github.com/p2pderivatives/rust-lightning/", rev = "698be7fb" } -lightning-persister = { git = "https://github.com/p2pderivatives/rust-lightning/", rev = "698be7fb" } +lightning = { git = "https://github.com/p2pderivatives/rust-lightning/", rev = "a57281b" } +lightning-background-processor = { git = "https://github.com/p2pderivatives/rust-lightning/", rev = "a57281b" } +lightning-transaction-sync = { git = "https://github.com/p2pderivatives/rust-lightning/", rev = "a57281b" } +lightning-net-tokio = { git = "https://github.com/p2pderivatives/rust-lightning/", rev = "a57281b" } +lightning-persister = { git = "https://github.com/p2pderivatives/rust-lightning/", rev = "a57281b" } rust-bitcoin-coin-selection = { git = "https://github.com/p2pderivatives/rust-bitcoin-coin-selection" } diff --git a/coordinator/Cargo.toml b/coordinator/Cargo.toml index f58af4abf..9dd77cdab 100644 --- a/coordinator/Cargo.toml +++ b/coordinator/Cargo.toml @@ -5,7 +5,7 @@ edition = "2021" [dependencies] atty = "0.2.14" -bitcoin = "0.29" +bitcoin = "0.29.2" console-subscriber = "0.1.6" diesel_migrations = "2.0.0" dlc = "0.4.0" @@ -44,7 +44,7 @@ version = "0.6.20" features = ["ws", "query"] [dependencies.bdk] -version = "0.27.0" +version = "0.28.0" default-features = false features = ["key-value-db", "use-esplora-blocking"] @@ -64,11 +64,11 @@ version = "0.4.0" features = ["use-serde"] [dependencies.lightning] -version = "0.0.114" +version = "0.0.116" features = ["max_level_trace"] [dependencies.lightning-invoice] -version = "0.22" +version = "0.24" [dependencies.ln-dlc-node] path = "../crates/ln-dlc-node" diff --git a/coordinator/src/admin.rs b/coordinator/src/admin.rs index 0755b375d..56b48e72e 100644 --- a/coordinator/src/admin.rs +++ b/coordinator/src/admin.rs @@ -13,7 +13,7 @@ use bdk::TransactionDetails; use bitcoin::secp256k1::PublicKey; use coordinator_commons::CollaborativeRevert; use dlc_manager::subchannel::SubChannel; -use lightning_invoice::Invoice; +use lightning_invoice::Bolt11Invoice; use ln_dlc_node::node::NodeInfo; use serde::de; use serde::Deserialize; @@ -172,7 +172,7 @@ pub async fn collaborative_revert( .map_err(move |error| { tracing::error!( channel_id = channel_id_string, - "Could not collaborativel revert channel. {error:#}" + "Could not collaboratively revert channel: {error:#}" ); AppError::InternalServerError("Could not collaboratively revert channel".to_string()) })?; @@ -272,7 +272,7 @@ pub async fn send_payment( Path(invoice): Path, State(state): State>, ) -> Result<(), AppError> { - let invoice = Invoice::from_str(invoice.as_str()) + let invoice = Bolt11Invoice::from_str(invoice.as_str()) .context("Could not parse Invoice string") .map_err(|e| AppError::BadRequest(format!("{e:#}")))?; state diff --git a/coordinator/src/bin/coordinator.rs b/coordinator/src/bin/coordinator.rs index b56b8be2e..67d78cf6a 100644 --- a/coordinator/src/bin/coordinator.rs +++ b/coordinator/src/bin/coordinator.rs @@ -25,7 +25,7 @@ use coordinator::settings::Settings; use diesel::r2d2; use diesel::r2d2::ConnectionManager; use diesel::PgConnection; -use lightning::util::events::Event; +use lightning::events::Event; use ln_dlc_node::scorer; use ln_dlc_node::seed::Bip39Seed; use ln_dlc_node::CoordinatorEventHandler; @@ -121,7 +121,7 @@ async fn main() -> Result<()> { )?); let event_handler = CoordinatorEventHandler::new(node.clone(), Some(node_event_sender)); - let running = node.start(event_handler)?; + let running = node.start(event_handler, false)?; let node = Node::new(node, running, pool.clone(), settings.to_node_settings()); // TODO: Pass the tokio metrics into Prometheus diff --git a/coordinator/src/collaborative_revert.rs b/coordinator/src/collaborative_revert.rs index d34025bae..188ea85cf 100644 --- a/coordinator/src/collaborative_revert.rs +++ b/coordinator/src/collaborative_revert.rs @@ -9,6 +9,7 @@ use anyhow::bail; use anyhow::Context; use axum::Json; use bdk::bitcoin::Transaction; +use bitcoin::hashes::hex::ToHex; use bitcoin::secp256k1::Secp256k1; use bitcoin::Amount; use coordinator_commons::CollaborativeRevert; @@ -19,9 +20,10 @@ use diesel::r2d2::PooledConnection; use diesel::PgConnection; use dlc::util::weight_to_fee; use dlc_manager::subchannel::LNChannelManager; +use dlc_manager::subchannel::LnDlcChannelSigner; +use dlc_manager::subchannel::LnDlcSignerProvider; use dlc_manager::subchannel::SubChannelState; use dlc_manager::Storage; -use lightning::util::errors::APIError; use ln_dlc_node::node::Node; use orderbook_commons::Message; use rust_decimal::prelude::ToPrimitive; @@ -230,116 +232,92 @@ pub fn confirm_collaborative_revert( channel_id: [u8; 32], inner_node: Arc>, ) -> anyhow::Result { - tracing::debug!( - channel_id = revert_params.channel_id, + let channel_id_hex = channel_id.to_hex(); + + tracing::info!( + channel_id = channel_id_hex, txid = revert_params.transaction.txid().to_string(), "Confirming collaborative revert" ); - // TODO: check if provided amounts are as expected + + // TODO: Check if provided amounts are as expected. if !revert_params .transaction .output .iter() .any(|output| inner_node.wallet().is_mine(&output.script_pubkey).is_ok()) { - let error_message = "Invalid request: no address for coordinator provided".to_string(); - tracing::error!(error_message); - bail!(error_message); + bail!("Proposed collaborative revert transaction doesn't pay the coordinator"); } - let sub_channels = inner_node + let subchannels = inner_node .list_dlc_channels() - .context("Failed to list dlc channels")?; - let sub_channel = sub_channels + .context("Failed to list subchannels")?; + let subchannel = subchannels .iter() .find(|c| c.channel_id == channel_id) - .context("Could not find provided channel")?; + .with_context(|| format!("Could not find subchannel {channel_id_hex}"))?; - let channel_manager = inner_node.channel_manager.clone(); + let mut revert_transaction = revert_params.transaction.clone(); - let mut own_sig = None; + let position = Position::get_position_by_trader(conn, subchannel.counter_party, vec![])? + .with_context(|| format!("Could not load position for subchannel {channel_id_hex}"))?; - let mut revert_transaction = revert_params.transaction.clone(); + let own_sig = { + let ln_channel_details = inner_node + .channel_manager + .get_channel_details(&subchannel.channel_id) + .with_context(|| { + format!("Could not get channel details for subchannel {channel_id_hex}") + })?; - let position = Position::get_position_by_trader(conn, sub_channel.counter_party, vec![])? - .context("Could not load position for channel_id")?; - - channel_manager - .with_channel_lock_no_check( - &sub_channel.channel_id, - &sub_channel.counter_party, - |channel_lock| { - channel_manager.sign_with_fund_key_cb(channel_lock, &mut |fund_sk| { - let secp = Secp256k1::new(); - - own_sig = Some( - dlc::util::get_raw_sig_for_tx_input( - &secp, - &revert_transaction, - 0, - &sub_channel.original_funding_redeemscript, - sub_channel.fund_value_satoshis, - fund_sk, - ) - .expect("To be able to get raw sig for tx inpout"), - ); - - dlc::util::sign_multi_sig_input( - &secp, - &mut revert_transaction, - &revert_params.signature, - &sub_channel.counter_fund_pk, - fund_sk, - &sub_channel.original_funding_redeemscript, - sub_channel.fund_value_satoshis, - 0, - ) - .expect("To be able to sign multi sig"); - }); - Ok(()) - }, - ) - .map_err(|error| { - let error = match error { - APIError::APIMisuseError { .. } => "APIMisuseError", - APIError::FeeRateTooHigh { .. } => "FeeRateTooHigh", - APIError::InvalidRoute { .. } => "InvalidRoute", - APIError::ChannelUnavailable { .. } => "ChannelUnavailable", - APIError::MonitorUpdateInProgress => "MonitorUpdateInProgress", - APIError::IncompatibleShutdownScript { .. } => "IncompatibleShutdownScript", - APIError::ExternalError { .. } => "ExternalError", - }; - tracing::error!("Could not get channel lock {error:#}"); - anyhow!("Could not get channel lock") - })?; - - // if we have a sig here, it means we were able to sign the transaction and can broadcast it - if own_sig.is_some() { - tracing::info!( - txid = revert_transaction.txid().to_string(), - "Broadcasting collaborative revert transaction" + let signer = inner_node.keys_manager.derive_ln_dlc_channel_signer( + subchannel.fund_value_satoshis, + ln_channel_details.channel_keys_id, ); - inner_node - .wallet() - .broadcast_transaction(&revert_transaction) - .context("Could not broadcast transaction")?; - Position::set_position_to_closed(conn, position.id) - .context("Could not set position to closed")?; + signer + .get_holder_split_tx_signature( + &Secp256k1::new(), + &revert_transaction, + &subchannel.original_funding_redeemscript, + subchannel.fund_value_satoshis, + ) + .context("Could not get own signature for collaborative revert transaction")? + }; - let mut sub_channel = sub_channel.clone(); + dlc::util::finalize_multi_sig_input_transaction( + &mut revert_transaction, + vec![ + (subchannel.own_fund_pk, own_sig), + (subchannel.counter_fund_pk, revert_params.signature), + ], + &subchannel.original_funding_redeemscript, + 0, + ); - sub_channel.state = SubChannelState::OnChainClosed; - inner_node - .sub_channel_manager - .get_dlc_manager() - .get_store() - .upsert_sub_channel(&sub_channel)?; + tracing::info!( + txid = revert_transaction.txid().to_string(), + "Broadcasting collaborative revert transaction" + ); + inner_node + .wallet() + .broadcast_transaction(&revert_transaction) + .context("Could not broadcast transaction")?; - db::collaborative_reverts::delete(conn, channel_id)?; + Position::set_position_to_closed(conn, position.id) + .context("Could not set position to closed")?; - Ok(revert_transaction) - } else { - bail!("Failed to sign revert transaction") - } + let mut sub_channel = subchannel.clone(); + + sub_channel.state = SubChannelState::OnChainClosed; + inner_node + .sub_channel_manager + .get_dlc_manager() + .get_store() + .upsert_sub_channel(&sub_channel)?; + + db::collaborative_reverts::delete(conn, channel_id)?; + + Ok(revert_transaction) } diff --git a/coordinator/src/db/spendable_outputs.rs b/coordinator/src/db/spendable_outputs.rs index 34dcfa7b1..804d758f1 100644 --- a/coordinator/src/db/spendable_outputs.rs +++ b/coordinator/src/db/spendable_outputs.rs @@ -5,10 +5,10 @@ use anyhow::Result; use bitcoin::hashes::hex::FromHex; use bitcoin::hashes::hex::ToHex; use diesel::prelude::*; -use lightning::chain::keysinterface::DelayedPaymentOutputDescriptor; -use lightning::chain::keysinterface::SpendableOutputDescriptor; -use lightning::chain::keysinterface::StaticPaymentOutputDescriptor; use lightning::chain::transaction::OutPoint; +use lightning::sign::DelayedPaymentOutputDescriptor; +use lightning::sign::SpendableOutputDescriptor; +use lightning::sign::StaticPaymentOutputDescriptor; use lightning::util::ser::Readable; use lightning::util::ser::Writeable; diff --git a/coordinator/src/node.rs b/coordinator/src/node.rs index 118d0d5eb..044fac5f9 100644 --- a/coordinator/src/node.rs +++ b/coordinator/src/node.rs @@ -39,6 +39,7 @@ use lightning::ln::channelmanager::ChannelDetails; use lightning::util::config::UserConfig; use ln_dlc_node::node; use ln_dlc_node::node::dlc_message_name; +use ln_dlc_node::node::send_dlc_message; use ln_dlc_node::node::sub_channel_message_name; use ln_dlc_node::node::RunningNode; use ln_dlc_node::WalletSettings; @@ -568,7 +569,12 @@ impl Node { "Sending message" ); - self.inner.dlc_message_handler.send_message(node_id, msg); + send_dlc_message( + &self.inner.dlc_message_handler, + &self.inner.peer_manager, + node_id, + msg, + ); } Ok(()) diff --git a/coordinator/src/node/channel_opening_fee.rs b/coordinator/src/node/channel_opening_fee.rs index d0a0c3369..5417b6ae0 100644 --- a/coordinator/src/node/channel_opening_fee.rs +++ b/coordinator/src/node/channel_opening_fee.rs @@ -5,7 +5,7 @@ use anyhow::Result; use bitcoin::hashes::hex::ToHex; use bitcoin::secp256k1::ThirtyTwoByteHash; use lightning::ln::PaymentHash; -use lightning_invoice::Invoice; +use lightning_invoice::Bolt11Invoice; use ln_dlc_node::channel::JIT_FEE_INVOICE_DESCRIPTION_PREFIX; use ln_dlc_node::PaymentInfo; @@ -15,7 +15,7 @@ impl Node { amount: u64, funding_txid: String, expiry: Option, - ) -> Result { + ) -> Result { let description = format!("{JIT_FEE_INVOICE_DESCRIPTION_PREFIX}{funding_txid}"); let invoice = self .inner diff --git a/coordinator/src/node/order_matching_fee.rs b/coordinator/src/node/order_matching_fee.rs new file mode 100644 index 000000000..8472795d5 --- /dev/null +++ b/coordinator/src/node/order_matching_fee.rs @@ -0,0 +1,43 @@ +use crate::db; +use crate::node::Node; +use anyhow::Context; +use anyhow::Result; +use bitcoin::secp256k1::ThirtyTwoByteHash; +use coordinator_commons::TradeParams; +use lightning::ln::PaymentHash; +use lightning_invoice::Bolt11Invoice; +use ln_dlc_node::PaymentInfo; +use orderbook_commons::order_matching_fee_taker; +use orderbook_commons::FEE_INVOICE_DESCRIPTION_PREFIX_TAKER; + +/// How long the fee invoice will last for. +const INVOICE_EXPIRY: u32 = 3600; + +impl Node { + pub async fn fee_invoice_taker( + &self, + trade_params: &TradeParams, + ) -> Result<(PaymentHash, Bolt11Invoice)> { + let order_id = trade_params.filled_with.order_id; + let description = format!("{FEE_INVOICE_DESCRIPTION_PREFIX_TAKER}{order_id}"); + + let fee = order_matching_fee_taker( + trade_params.quantity, + trade_params.average_execution_price(), + ) + .to_sat(); + + let invoice = self + .inner + .create_invoice(fee, description, INVOICE_EXPIRY)?; + + let fee_payment_hash = PaymentHash((*invoice.payment_hash()).into_32()); + let fee_payment_info = PaymentInfo::from(invoice.clone()); + let mut conn = self.pool.get()?; + + db::payments::insert((fee_payment_hash, fee_payment_info), &mut conn) + .context("Failed to insert payment into database")?; + + Ok((fee_payment_hash, invoice)) + } +} diff --git a/coordinator/src/node/routing_fees.rs b/coordinator/src/node/routing_fees.rs index 95ade4b09..12d7fa7c4 100644 --- a/coordinator/src/node/routing_fees.rs +++ b/coordinator/src/node/routing_fees.rs @@ -1,7 +1,7 @@ use crate::db; use crate::node::Node; use crate::routing_fee::models::NewRoutingFee; -use lightning::util::events::Event; +use lightning::events::Event; /// Save the routing fee in the database upon `PaymentForwarded` event /// diff --git a/coordinator/src/node/storage.rs b/coordinator/src/node/storage.rs index 932eea014..de51bdc64 100644 --- a/coordinator/src/node/storage.rs +++ b/coordinator/src/node/storage.rs @@ -6,11 +6,11 @@ use bitcoin::secp256k1::PublicKey; use diesel::r2d2::ConnectionManager; use diesel::r2d2::Pool; use diesel::PgConnection; -use lightning::chain::keysinterface::SpendableOutputDescriptor; use lightning::chain::transaction::OutPoint; use lightning::ln::PaymentHash; use lightning::ln::PaymentPreimage; use lightning::ln::PaymentSecret; +use lightning::sign::SpendableOutputDescriptor; use ln_dlc_node::channel::Channel; use ln_dlc_node::node; use ln_dlc_node::transaction::Transaction; diff --git a/crates/coordinator-commons/Cargo.toml b/crates/coordinator-commons/Cargo.toml index 55c9dc168..699905053 100644 --- a/crates/coordinator-commons/Cargo.toml +++ b/crates/coordinator-commons/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" [dependencies] anyhow = "1" -bdk = { version = "0.27.0", default-features = false, features = ["key-value-db", "use-esplora-blocking"] } +bdk = { version = "0.28.0", default-features = false, features = ["key-value-db", "use-esplora-blocking"] } orderbook-commons = { path = "../orderbook-commons" } rust_decimal = { version = "1", features = ["serde-with-float"] } serde = { version = "1", features = ["derive"] } diff --git a/crates/ln-dlc-node/Cargo.toml b/crates/ln-dlc-node/Cargo.toml index 2a0e6119a..5f7200e46 100644 --- a/crates/ln-dlc-node/Cargo.toml +++ b/crates/ln-dlc-node/Cargo.toml @@ -10,24 +10,24 @@ description = "A common interface for using Lightning and DLC channels side-by-s anyhow = { version = "1", features = ["backtrace"] } async-trait = "0.1.71" autometrics = "0.5" -bdk = { version = "0.27.0", default-features = false, features = ["key-value-db", "use-esplora-blocking"] } +bdk = { version = "0.28.0", default-features = false, features = ["key-value-db", "use-esplora-blocking", "std"] } bip39 = { version = "2", features = ["rand_core"] } -bitcoin = "0.29" +bitcoin = "0.29.2" dlc = { version = "0.4.0" } dlc-manager = { version = "0.4.0", features = ["use-serde"] } dlc-messages = { version = "0.4.0" } dlc-sled-storage-provider = { version = "0.1.0", features = ["wallet"] } dlc-trie = { version = "0.4.0" } -esplora-client = { version = "0.3", default-features = false } +esplora-client = { version = "0.4", default-features = false } futures = "0.3" hex = "0.4" hkdf = "0.12" -lightning = { version = "0.0.114", features = ["max_level_trace"] } -lightning-background-processor = { version = "0.0.114", features = ["futures"] } -lightning-invoice = { version = "0.22" } -lightning-net-tokio = { version = "0.0.114" } -lightning-persister = { version = "0.0.114" } -lightning-transaction-sync = { version = "0.0.114", features = ["esplora-blocking"] } +lightning = { version = "0.0.116", features = ["max_level_trace", "std"] } +lightning-background-processor = { version = "0.0.116", features = ["futures"] } +lightning-invoice = { version = "0.24" } +lightning-net-tokio = { version = "0.0.116" } +lightning-persister = { version = "0.0.116" } +lightning-transaction-sync = { version = "0.0.116", features = ["esplora-blocking"] } log = "0.4.17" p2pd-oracle-client = { version = "0.1.0" } parking_lot = { version = "0.12.1" } diff --git a/crates/ln-dlc-node/src/channel.rs b/crates/ln-dlc-node/src/channel.rs index 42f1fcb28..0bde44890 100644 --- a/crates/ln-dlc-node/src/channel.rs +++ b/crates/ln-dlc-node/src/channel.rs @@ -4,8 +4,8 @@ use bitcoin::hashes::hex::ToHex; use bitcoin::secp256k1::PublicKey; use bitcoin::Txid; use dlc_manager::ChannelId; +use lightning::events::ClosureReason; use lightning::ln::channelmanager::ChannelDetails; -use lightning::util::events::ClosureReason; use std::fmt; use std::fmt::Display; use std::fmt::Formatter; diff --git a/crates/ln-dlc-node/src/dlc_custom_signer.rs b/crates/ln-dlc-node/src/dlc_custom_signer.rs index 02ef6bd94..b5b45f415 100644 --- a/crates/ln-dlc-node/src/dlc_custom_signer.rs +++ b/crates/ln-dlc-node/src/dlc_custom_signer.rs @@ -7,21 +7,22 @@ use anyhow::Result; use bitcoin::Script; use bitcoin::Transaction; use bitcoin::TxOut; -use lightning::chain::keysinterface::ChannelSigner; -use lightning::chain::keysinterface::EcdsaChannelSigner; -use lightning::chain::keysinterface::EntropySource; -use lightning::chain::keysinterface::ExtraSign; -use lightning::chain::keysinterface::InMemorySigner; -use lightning::chain::keysinterface::KeyMaterial; -use lightning::chain::keysinterface::KeysManager; -use lightning::chain::keysinterface::NodeSigner; -use lightning::chain::keysinterface::Recipient; -use lightning::chain::keysinterface::SignerProvider; -use lightning::chain::keysinterface::SpendableOutputDescriptor; -use lightning::chain::keysinterface::WriteableEcdsaChannelSigner; +use dlc_manager::subchannel::LnDlcChannelSigner; +use dlc_manager::subchannel::LnDlcSignerProvider; use lightning::ln::chan_utils::ChannelPublicKeys; use lightning::ln::msgs::DecodeError; use lightning::ln::script::ShutdownScript; +use lightning::sign::ChannelSigner; +use lightning::sign::EcdsaChannelSigner; +use lightning::sign::EntropySource; +use lightning::sign::InMemorySigner; +use lightning::sign::KeyMaterial; +use lightning::sign::KeysManager; +use lightning::sign::NodeSigner; +use lightning::sign::Recipient; +use lightning::sign::SignerProvider; +use lightning::sign::SpendableOutputDescriptor; +use lightning::sign::WriteableEcdsaChannelSigner; use lightning::util::ser::Writeable; use parking_lot::Mutex; use parking_lot::MutexGuard; @@ -135,6 +136,21 @@ impl EcdsaChannelSigner for CustomSigner { ) } + fn sign_holder_htlc_transaction( + &self, + htlc_tx: &Transaction, + input: usize, + htlc_descriptor: &lightning::events::bump_transaction::HTLCDescriptor, + secp_ctx: &Secp256k1, + ) -> Result { + self.in_memory_signer_lock().sign_holder_htlc_transaction( + htlc_tx, + input, + htlc_descriptor, + secp_ctx, + ) + } + fn sign_counterparty_htlc_transaction( &self, htlc_tx: &Transaction, @@ -222,15 +238,6 @@ impl ChannelSigner for CustomSigner { self.in_memory_signer_lock() .provide_channel_parameters(channel_parameters); } -} - -impl ExtraSign for CustomSigner { - fn sign_with_fund_key_callback(&self, cb: &mut F) - where - F: FnMut(&SecretKey), - { - self.in_memory_signer_lock().sign_with_fund_key_callback(cb) - } fn set_channel_value_satoshis(&mut self, value: u64) { self.in_memory_signer_lock() @@ -238,6 +245,45 @@ impl ExtraSign for CustomSigner { } } +impl LnDlcChannelSigner for CustomSigner { + fn get_holder_split_tx_signature( + &self, + secp: &Secp256k1, + split_tx: &Transaction, + original_funding_redeemscript: &Script, + original_channel_value_satoshis: u64, + ) -> std::result::Result { + dlc::util::get_raw_sig_for_tx_input( + secp, + split_tx, + 0, + original_funding_redeemscript, + original_channel_value_satoshis, + &self.in_memory_signer_lock().funding_key, + ) + .map_err(|e| e.into()) + } + + fn get_holder_split_tx_adaptor_signature( + &self, + secp: &Secp256k1, + split_tx: &Transaction, + original_channel_value_satoshis: u64, + original_funding_redeemscript: &Script, + other_publish_key: &secp256k1_zkp::PublicKey, + ) -> std::result::Result { + dlc::channel::get_tx_adaptor_signature( + secp, + split_tx, + original_channel_value_satoshis, + original_funding_redeemscript, + &self.in_memory_signer_lock().funding_key, + other_publish_key, + ) + .map_err(|e| e.into()) + } +} + impl Writeable for CustomSigner { fn write(&self, writer: &mut W) -> Result<(), std::io::Error> { self.in_memory_signer_lock().write(writer) @@ -278,28 +324,42 @@ impl CustomKeysManager { outputs, change_destination_script, feerate_sat_per_1000_weight, + None, secp_ctx, ) .map_err(|_| anyhow!("Could not spend spendable outputs")) } } +impl LnDlcSignerProvider for CustomKeysManager { + fn derive_ln_dlc_channel_signer( + &self, + channel_value_satoshis: u64, + channel_keys_id: [u8; 32], + ) -> CustomSigner { + self.derive_channel_signer(channel_value_satoshis, channel_keys_id) + } +} + impl SignerProvider for CustomKeysManager { type Signer = CustomSigner; - fn get_destination_script(&self) -> Script { + fn get_destination_script(&self) -> Result { let address = self.wallet.unused_address(); - address.script_pubkey() + Ok(address.script_pubkey()) } - fn get_shutdown_scriptpubkey(&self) -> ShutdownScript { + fn get_shutdown_scriptpubkey(&self) -> std::result::Result { let address = self.wallet.unused_address(); match address.payload { bitcoin::util::address::Payload::WitnessProgram { version, program } => { ShutdownScript::new_witness_program(version, &program) - .expect("Invalid shutdown script.") + .map_err(|_ignored| tracing::error!("Invalid shutdown script")) + } + _ => { + tracing::error!("Tried to use a non-witness address. This must not ever happen."); + Err(()) } - _ => panic!("Tried to use a non-witness address. This must not ever happen."), } } diff --git a/crates/ln-dlc-node/src/fee_rate_estimator.rs b/crates/ln-dlc-node/src/fee_rate_estimator.rs index 1091f3190..e2a3e5751 100644 --- a/crates/ln-dlc-node/src/fee_rate_estimator.rs +++ b/crates/ln-dlc-node/src/fee_rate_estimator.rs @@ -6,10 +6,13 @@ use lightning::chain::chaininterface::FEERATE_FLOOR_SATS_PER_KW; use parking_lot::RwLock; use std::collections::HashMap; -const CONFIRMATION_TARGETS: [(ConfirmationTarget, usize); 3] = [ +const CONFIRMATION_TARGETS: [(ConfirmationTarget, usize); 4] = [ // We choose an extremely high background confirmation target to avoid force-closing channels // unnecessarily. (ConfirmationTarget::Background, 1008), + // We just want to end up in the mempool eventually. We just set the target to 1008 + // as that is esplora's highest block target available + (ConfirmationTarget::MempoolMinimum, 1008), (ConfirmationTarget::Normal, 6), (ConfirmationTarget::HighPriority, 3), ]; diff --git a/crates/ln-dlc-node/src/ldk_node_wallet.rs b/crates/ln-dlc-node/src/ldk_node_wallet.rs index f58037b89..b67fb84cb 100644 --- a/crates/ln-dlc-node/src/ldk_node_wallet.rs +++ b/crates/ln-dlc-node/src/ldk_node_wallet.rs @@ -295,12 +295,14 @@ where B: Blockchain, F: EstimateFeeRate, { - fn broadcast_transaction(&self, tx: &Transaction) { - if let Err(e) = self.broadcast_transaction(tx) { - tracing::error!( - txid = %tx.txid(), - "Error when broadcasting transaction: {e:#}" - ); + fn broadcast_transactions(&self, txs: &[&Transaction]) { + for tx in txs { + if let Err(e) = self.broadcast_transaction(tx) { + tracing::error!( + txid = %tx.txid(), + "Error when broadcasting transaction: {e:#}" + ); + } } } } @@ -507,7 +509,7 @@ pub mod tests { fn insert_spendable_output( &self, - _descriptor: lightning::chain::keysinterface::SpendableOutputDescriptor, + _descriptor: lightning::sign::SpendableOutputDescriptor, ) -> Result<()> { unimplemented!(); } @@ -515,7 +517,7 @@ pub mod tests { fn get_spendable_output( &self, _outpoint: &lightning::chain::transaction::OutPoint, - ) -> Result> { + ) -> Result> { unimplemented!(); } @@ -526,9 +528,7 @@ pub mod tests { unimplemented!(); } - fn all_spendable_outputs( - &self, - ) -> Result> { + fn all_spendable_outputs(&self) -> Result> { unimplemented!(); } diff --git a/crates/ln-dlc-node/src/lib.rs b/crates/ln-dlc-node/src/lib.rs index 6a3c40162..a94fe49ff 100644 --- a/crates/ln-dlc-node/src/lib.rs +++ b/crates/ln-dlc-node/src/lib.rs @@ -7,15 +7,17 @@ use dlc_messages::message_handler::MessageHandler as DlcMessageHandler; use fee_rate_estimator::FeeRateEstimator; use lightning::chain::chainmonitor; use lightning::chain::Filter; +use lightning::ln::peer_handler::IgnoringMessageHandler; use lightning::ln::PaymentPreimage; use lightning::ln::PaymentSecret; use lightning::routing::gossip; use lightning::routing::gossip::P2PGossipSync; use lightning::routing::router::DefaultRouter; use lightning::routing::scoring::ProbabilisticScorer; +use lightning::routing::scoring::ProbabilisticScoringFeeParameters; use lightning::routing::utxo::UtxoLookup; -use lightning_invoice::Invoice; -use lightning_invoice::InvoiceDescription; +use lightning_invoice::Bolt11Invoice; +use lightning_invoice::Bolt11InvoiceDescription; use lightning_net_tokio::SocketDescriptor; use lightning_persister::FilesystemPersister; use ln_dlc_wallet::LnDlcWallet; @@ -76,7 +78,7 @@ pub type PeerManager = lightning::ln::peer_handler::PeerManager< Arc, >, >, - Arc, + Arc, Arc, Arc, Arc, @@ -85,8 +87,11 @@ pub type PeerManager = lightning::ln::peer_handler::PeerManager< pub(crate) type Router = DefaultRouter< Arc, Arc, - Arc, Arc>>>, + Arc>, + ProbabilisticScoringFeeParameters, + Scorer, >; +pub(crate) type Scorer = ProbabilisticScorer, Arc>; type NetworkGraph = gossip::NetworkGraph>; @@ -131,8 +136,8 @@ impl MillisatAmount { } } -impl From for PaymentInfo { - fn from(value: Invoice) -> Self { +impl From for PaymentInfo { + fn from(value: Bolt11Invoice) -> Self { Self { preimage: None, secret: Some(*value.payment_secret()), @@ -142,8 +147,8 @@ impl From for PaymentInfo { flow: PaymentFlow::Inbound, timestamp: OffsetDateTime::from(value.timestamp()), description: match value.description() { - InvoiceDescription::Direct(direct) => direct.to_string(), - InvoiceDescription::Hash(hash) => hash.0.to_hex(), + Bolt11InvoiceDescription::Direct(direct) => direct.to_string(), + Bolt11InvoiceDescription::Hash(hash) => hash.0.to_hex(), }, invoice: Some(value.to_string()), } diff --git a/crates/ln-dlc-node/src/ln/app_event_handler.rs b/crates/ln-dlc-node/src/ln/app_event_handler.rs index cc8a09d22..b446a3e0c 100644 --- a/crates/ln-dlc-node/src/ln/app_event_handler.rs +++ b/crates/ln-dlc-node/src/ln/app_event_handler.rs @@ -15,7 +15,7 @@ use async_trait::async_trait; use bitcoin::hashes::hex::ToHex; use bitcoin::secp256k1::PublicKey; use dlc_manager::subchannel::LNChannelManager; -use lightning::util::events::Event; +use lightning::events::Event; use parking_lot::Mutex; use std::collections::HashMap; use std::sync::Arc; @@ -155,6 +155,7 @@ where next_channel_id, fee_earned_msat, claim_from_onchain_tx, + outbound_amount_forwarded_msat, } => { common_handlers::handle_payment_forwarded( &self.node, @@ -162,6 +163,7 @@ where next_channel_id, claim_from_onchain_tx, fee_earned_msat, + outbound_amount_forwarded_msat, ); } Event::PendingHTLCsForwardable { time_forwardable } => { @@ -234,10 +236,13 @@ where Event::PaymentClaimable { receiver_node_id: _, payment_hash, + onion_fields: _, amount_msat, + counterparty_skimmed_fee_msat: _, purpose, via_channel_id: _, via_user_channel_id: _, + claim_deadline: _, } => { common_handlers::handle_payment_claimable( &self.node.channel_manager, @@ -249,6 +254,27 @@ where Event::HTLCIntercepted { .. } => { unimplemented!("App should not intercept htlcs") } + Event::ChannelPending { + channel_id, + user_channel_id: _, + former_temporary_channel_id, + counterparty_node_id, + funding_txo, + } => { + let former_temporary_channel_id = + former_temporary_channel_id.unwrap_or([0; 32]).to_hex(); + tracing::debug!( + channel_id = channel_id.to_hex(), + former_temporary_channel_id, + counterparty_node_id = counterparty_node_id.to_string(), + funding_txo_tx_id = funding_txo.txid.to_string(), + funding_txo_tx_vout = funding_txo.vout, + "Channel pending" + ) + } + Event::BumpTransaction(_) => { + tracing::error!("We do not support anchor outputs yet"); + } }; Ok(()) diff --git a/crates/ln-dlc-node/src/ln/channel_details.rs b/crates/ln-dlc-node/src/ln/channel_details.rs index 645a9a45a..f23a9b37f 100644 --- a/crates/ln-dlc-node/src/ln/channel_details.rs +++ b/crates/ln-dlc-node/src/ln/channel_details.rs @@ -36,12 +36,32 @@ pub struct ChannelDetails { pub scid: Option, } +/// Copy of ['lightning::util::config::MaxDustHTLCExposure'] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize)] +pub enum MaxDustHTLCExposure { + FixedLimitMsat(u64), + FeeRateMultiplier(u64), +} + +impl From for MaxDustHTLCExposure { + fn from(value: lightning::util::config::MaxDustHTLCExposure) -> Self { + match value { + lightning::util::config::MaxDustHTLCExposure::FixedLimitMsat(val) => { + MaxDustHTLCExposure::FixedLimitMsat(val) + } + lightning::util::config::MaxDustHTLCExposure::FeeRateMultiplier(val) => { + MaxDustHTLCExposure::FeeRateMultiplier(val) + } + } + } +} + #[derive(Serialize, Debug)] pub struct ChannelConfig { pub forwarding_fee_proportional_millionths: u32, pub forwarding_fee_base_msat: u32, pub cltv_expiry_delta: u16, - pub max_dust_htlc_exposure_msat: u64, + pub max_dust_htlc_exposure_msat: MaxDustHTLCExposure, pub force_close_avoidance_max_fee_satoshis: u64, } @@ -72,7 +92,7 @@ impl From for ChannelDetails { forwarding_fee_proportional_millionths: c.forwarding_fee_proportional_millionths, forwarding_fee_base_msat: c.forwarding_fee_base_msat, cltv_expiry_delta: c.cltv_expiry_delta, - max_dust_htlc_exposure_msat: c.max_dust_htlc_exposure_msat, + max_dust_htlc_exposure_msat: c.max_dust_htlc_exposure.into(), force_close_avoidance_max_fee_satoshis: c.force_close_avoidance_max_fee_satoshis, }), scid: cd.short_channel_id, diff --git a/crates/ln-dlc-node/src/ln/common_handlers.rs b/crates/ln-dlc-node/src/ln/common_handlers.rs index 6ad777df5..894666070 100644 --- a/crates/ln-dlc-node/src/ln/common_handlers.rs +++ b/crates/ln-dlc-node/src/ln/common_handlers.rs @@ -18,11 +18,11 @@ use bitcoin::secp256k1::PublicKey; use lightning::chain::chaininterface::BroadcasterInterface; use lightning::chain::chaininterface::ConfirmationTarget; use lightning::chain::chaininterface::FeeEstimator; -use lightning::chain::keysinterface::SpendableOutputDescriptor; +use lightning::events::PaymentPurpose; use lightning::ln::channelmanager::InterceptId; use lightning::ln::PaymentHash; use lightning::routing::gossip::NodeId; -use lightning::util::events::PaymentPurpose; +use lightning::sign::SpendableOutputDescriptor; use rand::thread_rng; use rand::Rng; use secp256k1_zkp::Secp256k1; @@ -57,7 +57,7 @@ pub fn handle_payment_claimable( pub fn handle_htlc_handling_failed( prev_channel_id: [u8; 32], - failed_next_destination: lightning::util::events::HTLCDestination, + failed_next_destination: lightning::events::HTLCDestination, ) { tracing::info!( prev_channel_id = %prev_channel_id.to_hex(), @@ -86,25 +86,23 @@ pub fn handle_payment_forwarded( next_channel_id: Option<[u8; 32]>, claim_from_onchain_tx: bool, fee_earned_msat: Option, + outbound_amount_forwarded_msat: Option, ) { let read_only_network_graph = node.network_graph.read_only(); let nodes = read_only_network_graph.nodes(); let channels = node.channel_manager.list_channels(); - let node_str = |channel_id: &Option<[u8; 32]>| match channel_id { - None => String::new(), - Some(channel_id) => match channels.iter().find(|c| c.channel_id == *channel_id) { - None => String::new(), - Some(channel) => match nodes.get(&NodeId::from_pubkey(&channel.counterparty.node_id)) { - None => " from private node".to_string(), - Some(node) => match &node.announcement_info { - None => " from unnamed node".to_string(), - Some(announcement) => { - format!("node {}", announcement.alias) - } - }, - }, - }, + let node_str = |channel_id: &Option<[u8; 32]>| { + channel_id + .and_then(|channel_id| channels.iter().find(|c| c.channel_id == channel_id)) + .and_then(|channel| nodes.get(&NodeId::from_pubkey(&channel.counterparty.node_id))) + .map_or("private_node".to_string(), |node| { + node.announcement_info + .as_ref() + .map_or("unnamed node".to_string(), |ann| { + format!("node {}", ann.alias) + }) + }) }; let channel_str = |channel_id: &Option<[u8; 32]>| { channel_id @@ -112,35 +110,33 @@ pub fn handle_payment_forwarded( .unwrap_or_default() }; let from_prev_str = format!( - "{}{}", + " from {}{}", node_str(&prev_channel_id), channel_str(&prev_channel_id) ); let to_next_str = format!( - "{}{}", + " to {}{}", node_str(&next_channel_id), channel_str(&next_channel_id) ); - let from_onchain_str = if claim_from_onchain_tx { - "from onchain downstream claim" - } else { - "from HTLC fulfill message" - }; - if let Some(fee_earned) = fee_earned_msat { + let fee_earned = fee_earned_msat.unwrap_or(0); + let outbound_amount_forwarded_msat = outbound_amount_forwarded_msat.unwrap_or(0); + if claim_from_onchain_tx { tracing::info!( - "Forwarded payment{}{}, earning {} msat {}", + "Forwarded payment{}{} of {}msat, earning {}msat in fees from claiming onchain.", from_prev_str, to_next_str, + outbound_amount_forwarded_msat, fee_earned, - from_onchain_str ); } else { tracing::info!( - "Forwarded payment{}{}, claiming onchain {}", + "Forwarded payment{}{} of {}msat, earning {}msat in fees.", from_prev_str, to_next_str, - from_onchain_str + outbound_amount_forwarded_msat, + fee_earned, ); } } @@ -221,7 +217,7 @@ pub fn handle_channel_closed( node: &Arc>, pending_intercepted_htlcs: &PendingInterceptedHtlcs, user_channel_id: u128, - reason: lightning::util::events::ClosureReason, + reason: lightning::events::ClosureReason, channel_id: [u8; 32], ) -> Result<(), anyhow::Error> where @@ -239,7 +235,7 @@ where if let Some(channel) = node.storage.get_channel(&user_channel_id)? { let counterparty = channel.counterparty; - let channel = Channel::close_channel(channel, reason); + let channel = Channel::close_channel(channel, reason.clone()); node.storage.upsert_channel(channel)?; // Fail intercepted HTLC which was meant to be used to open the JIT channel, @@ -251,7 +247,7 @@ where match node .sub_channel_manager - .notify_ln_channel_closed(channel_id) + .notify_ln_channel_closed(channel_id, &reason) { Ok(()) => {} Err(dlc_manager::error::Error::InvalidParameters(msg)) => { @@ -301,7 +297,7 @@ where tx_feerate, &Secp256k1::new(), )?; - node.wallet.broadcast_transaction(&spending_tx); + node.wallet.broadcast_transactions(&[&spending_tx]); Ok(()) } diff --git a/crates/ln-dlc-node/src/ln/coordinator_event_handler.rs b/crates/ln-dlc-node/src/ln/coordinator_event_handler.rs index 8ac3ae7a5..e7b4de9d8 100644 --- a/crates/ln-dlc-node/src/ln/coordinator_event_handler.rs +++ b/crates/ln-dlc-node/src/ln/coordinator_event_handler.rs @@ -22,9 +22,9 @@ use bitcoin::hashes::hex::ToHex; use bitcoin::secp256k1::PublicKey; use dlc_manager::subchannel::LNChannelManager; use lightning::chain::chaininterface::FeeEstimator; +use lightning::events::Event; use lightning::ln::channelmanager::InterceptId; use lightning::ln::PaymentHash; -use lightning::util::events::Event; use parking_lot::Mutex; use rust_decimal::prelude::ToPrimitive; use rust_decimal::Decimal; @@ -143,6 +143,7 @@ where next_channel_id, fee_earned_msat, claim_from_onchain_tx, + outbound_amount_forwarded_msat, } => { common_handlers::handle_payment_forwarded( &self.node, @@ -150,6 +151,7 @@ where next_channel_id, claim_from_onchain_tx, fee_earned_msat, + outbound_amount_forwarded_msat, ); } Event::PendingHTLCsForwardable { time_forwardable } => { @@ -227,10 +229,13 @@ where Event::PaymentClaimable { receiver_node_id: _, payment_hash, + onion_fields: _, amount_msat, + counterparty_skimmed_fee_msat: _, purpose, via_channel_id: _, via_user_channel_id: _, + claim_deadline: _, } => { common_handlers::handle_payment_claimable( &self.node.channel_manager, @@ -257,6 +262,27 @@ where ) .await?; } + Event::ChannelPending { + channel_id, + user_channel_id: _, + former_temporary_channel_id, + counterparty_node_id, + funding_txo, + } => { + let former_temporary_channel_id = + former_temporary_channel_id.unwrap_or([0; 32]).to_hex(); + tracing::debug!( + channel_id = channel_id.to_hex(), + former_temporary_channel_id, + counterparty_node_id = counterparty_node_id.to_string(), + funding_txo_tx_id = funding_txo.txid.to_string(), + funding_txo_tx_vout = funding_txo.vout, + "Channel pending" + ) + } + Event::BumpTransaction(_) => { + tracing::error!("We do not support anchor outputs yet"); + } }; Ok(()) diff --git a/crates/ln-dlc-node/src/ln/event_handler.rs b/crates/ln-dlc-node/src/ln/event_handler.rs index c09846298..7473bf34d 100644 --- a/crates/ln-dlc-node/src/ln/event_handler.rs +++ b/crates/ln-dlc-node/src/ln/event_handler.rs @@ -1,8 +1,8 @@ use anyhow::Result; use async_trait::async_trait; use bitcoin::secp256k1::PublicKey; +use lightning::events::Event; use lightning::ln::channelmanager::InterceptId; -use lightning::util::events::Event; use parking_lot::Mutex; use std::collections::HashMap; use std::sync::Arc; diff --git a/crates/ln-dlc-node/src/ln/manage_spendable_outputs.rs b/crates/ln-dlc-node/src/ln/manage_spendable_outputs.rs index a179e59bc..2975314ba 100644 --- a/crates/ln-dlc-node/src/ln/manage_spendable_outputs.rs +++ b/crates/ln-dlc-node/src/ln/manage_spendable_outputs.rs @@ -10,10 +10,10 @@ use esplora_client::TxStatus; use lightning::chain::chaininterface::BroadcasterInterface; use lightning::chain::chaininterface::ConfirmationTarget; use lightning::chain::chaininterface::FeeEstimator; -use lightning::chain::keysinterface::DelayedPaymentOutputDescriptor; -use lightning::chain::keysinterface::SpendableOutputDescriptor; -use lightning::chain::keysinterface::StaticPaymentOutputDescriptor; use lightning::chain::transaction::OutPoint; +use lightning::sign::DelayedPaymentOutputDescriptor; +use lightning::sign::SpendableOutputDescriptor; +use lightning::sign::StaticPaymentOutputDescriptor; use secp256k1_zkp::Secp256k1; use std::borrow::Borrow; use std::sync::Arc; @@ -74,7 +74,7 @@ pub fn manage_spendable_outputs( tx_feerate, &Secp256k1::new(), )?; - wallet.borrow().broadcast_transaction(&spending_tx); + wallet.borrow().broadcast_transactions(&[&spending_tx]); Ok(()) } diff --git a/crates/ln-dlc-node/src/ln_dlc_wallet.rs b/crates/ln-dlc-node/src/ln_dlc_wallet.rs index 97fcf6ece..d24a4545f 100644 --- a/crates/ln-dlc-node/src/ln_dlc_wallet.rs +++ b/crates/ln-dlc-node/src/ln_dlc_wallet.rs @@ -295,12 +295,14 @@ impl dlc_manager::Wallet for LnDlcWallet { } impl BroadcasterInterface for LnDlcWallet { - fn broadcast_transaction(&self, tx: &Transaction) { - if let Err(e) = self.ln_wallet.broadcast_transaction(tx) { - tracing::error!( - txid = %tx.txid(), - "Error when broadcasting transaction: {e:#}" - ); + fn broadcast_transactions(&self, txs: &[&Transaction]) { + for tx in txs { + if let Err(e) = self.ln_wallet.broadcast_transaction(tx) { + tracing::error!( + txid = %tx.txid(), + "Error when broadcasting transaction: {e:#}" + ); + } } } } diff --git a/crates/ln-dlc-node/src/node/channel_manager.rs b/crates/ln-dlc-node/src/node/channel_manager.rs index 8fde93194..13ef062b6 100644 --- a/crates/ln-dlc-node/src/node/channel_manager.rs +++ b/crates/ln-dlc-node/src/node/channel_manager.rs @@ -69,6 +69,9 @@ pub(crate) fn build( network, best_block: BestBlock::new(block_hash, height), }, + std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH)? + .as_secs() as u32, )); } }; diff --git a/crates/ln-dlc-node/src/node/dlc_channel.rs b/crates/ln-dlc-node/src/node/dlc_channel.rs index 8c8e67b6d..3628b87fe 100644 --- a/crates/ln-dlc-node/src/node/dlc_channel.rs +++ b/crates/ln-dlc-node/src/node/dlc_channel.rs @@ -1,5 +1,6 @@ use crate::node::Node; use crate::DlcMessageHandler; +use crate::PeerManager; use crate::SubChannelManager; use crate::ToHex; use anyhow::anyhow; @@ -45,6 +46,7 @@ where let sub_channel_manager = self.sub_channel_manager.clone(); let event_id = contract_input.contract_infos[0].oracles.event_id.clone(); let dlc_message_handler = self.dlc_message_handler.clone(); + let peer_manager = self.peer_manager.clone(); move || { let announcement = oracle.get_announcement(&event_id)?; @@ -54,7 +56,9 @@ where &[vec![announcement]], )?; - dlc_message_handler.send_message( + send_dlc_message( + &dlc_message_handler, + &peer_manager, channel_details.counterparty.node_id, Message::SubChannel(SubChannelMessage::Offer(sub_channel_offer)), ); @@ -77,12 +81,15 @@ where spawn_blocking({ let dlc_manager = self.dlc_manager.clone(); let dlc_message_handler = self.dlc_message_handler.clone(); + let peer_manager = self.peer_manager.clone(); let dlc_channel_id = *dlc_channel_id; move || { let (renew_offer, counterparty_pubkey) = dlc_manager.renew_offer(&dlc_channel_id, payout_amount, &contract_input)?; - dlc_message_handler.send_message( + send_dlc_message( + &dlc_message_handler, + &peer_manager, counterparty_pubkey, Message::Channel(ChannelMessage::RenewOffer(renew_offer)), ); @@ -102,7 +109,9 @@ where let (node_id, accept_sub_channel) = self.sub_channel_manager.accept_sub_channel(channel_id)?; - self.dlc_message_handler.send_message( + send_dlc_message( + &self.dlc_message_handler, + &self.peer_manager, node_id, Message::SubChannel(SubChannelMessage::Accept(accept_sub_channel)), ); @@ -127,11 +136,14 @@ where spawn_blocking({ let sub_channel_manager = self.sub_channel_manager.clone(); let dlc_message_handler = self.dlc_message_handler.clone(); + let peer_manager = self.peer_manager.clone(); move || { let (sub_channel_close_offer, counterparty_pk) = sub_channel_manager .offer_subchannel_close(&channel_id, accept_settlement_amount)?; - dlc_message_handler.send_message( + send_dlc_message( + &dlc_message_handler, + &peer_manager, counterparty_pk, Message::SubChannel(SubChannelMessage::CloseOffer(sub_channel_close_offer)), ); @@ -152,7 +164,9 @@ where .sub_channel_manager .accept_subchannel_close_offer(channel_id)?; - self.dlc_message_handler.send_message( + send_dlc_message( + &self.dlc_message_handler, + &self.peer_manager, counterparty_pk, Message::SubChannel(SubChannelMessage::CloseAccept(sub_channel_close_accept)), ); @@ -409,6 +423,7 @@ where let dlc_message_handler = &self.dlc_message_handler; let dlc_manager = &self.dlc_manager; let sub_channel_manager = &self.sub_channel_manager; + let peer_manager = &self.peer_manager; let messages = dlc_message_handler.get_and_clear_received_messages(); tracing::debug!("Received and cleared {} messages", messages.len()); @@ -420,7 +435,7 @@ where if let Some(msg) = resp { tracing::debug!(to = %node_id, "Sending DLC-manager message"); - dlc_message_handler.send_message(node_id, msg); + send_dlc_message(dlc_message_handler, peer_manager, node_id, msg); } } Message::SubChannel(msg) => { @@ -437,7 +452,12 @@ where msg = %sub_channel_message_name(&msg), "Sending DLC channel message" ); - dlc_message_handler.send_message(node_id, Message::SubChannel(msg)); + send_dlc_message( + dlc_message_handler, + peer_manager, + node_id, + Message::SubChannel(msg), + ); } } } @@ -447,10 +467,31 @@ where } } +/// Ensure that a [`dlc_messages::Message`] is sent straight away. +/// +/// Use this instead of [`MessageHandler`]'s `send_message` which only enqueues the message. +/// +/// [`MessageHandler`]: dlc_messages::message_handler::MessageHandler +pub fn send_dlc_message( + dlc_message_handler: &DlcMessageHandler, + peer_manager: &PeerManager, + node_id: PublicKey, + msg: Message, +) { + // Enqueue the message. + dlc_message_handler.send_message(node_id, msg); + + // According to the LDK docs, you don't _have_ to call this function explicitly if you are + // using [`lightning-net-tokio`], which we are. But calling it ensures that we send the + // enqueued message ASAP. + peer_manager.process_events(); +} + #[autometrics] pub(crate) async fn sub_channel_manager_periodic_check( sub_channel_manager: Arc, dlc_message_handler: &DlcMessageHandler, + peer_manager: &PeerManager, ) -> Result<()> { let messages = spawn_blocking(move || sub_channel_manager.periodic_check()).await?; @@ -464,7 +505,7 @@ pub(crate) async fn sub_channel_manager_periodic_check( "Queuing up DLC channel message tied to pending action" ); - dlc_message_handler.send_message(node_id, msg); + send_dlc_message(dlc_message_handler, peer_manager, node_id, msg); } Ok(()) diff --git a/crates/ln-dlc-node/src/node/invoice.rs b/crates/ln-dlc-node/src/node/invoice.rs index bca385261..85ba09e3e 100644 --- a/crates/ln-dlc-node/src/node/invoice.rs +++ b/crates/ln-dlc-node/src/node/invoice.rs @@ -25,10 +25,10 @@ use lightning::routing::router::RouteHintHop; use lightning_invoice::payment::pay_invoice; use lightning_invoice::payment::pay_zero_value_invoice; use lightning_invoice::payment::PaymentError; +use lightning_invoice::Bolt11Invoice; +use lightning_invoice::Bolt11InvoiceDescription; use lightning_invoice::Currency; -use lightning_invoice::Invoice; use lightning_invoice::InvoiceBuilder; -use lightning_invoice::InvoiceDescription; use std::fmt; use std::fmt::Formatter; use std::time::Duration; @@ -44,7 +44,7 @@ where amount_in_sats: u64, description: String, expiry: u32, - ) -> Result { + ) -> Result { lightning_invoice::utils::create_invoice_from_channelmanager( &self.channel_manager, self.keys_manager.clone(), @@ -69,7 +69,7 @@ where invoice_expiry: Option, description: String, final_route_hint_hop: RouteHintHop, - ) -> Result { + ) -> Result { let invoice_expiry = invoice_expiry .unwrap_or_else(|| Duration::from_secs(lightning_invoice::DEFAULT_EXPIRY_TIME)); let amount_msat = amount_in_sats.map(|x| x * 1000); @@ -105,7 +105,7 @@ where Ok(secp_ctx.sign_ecdsa_recoverable(hash, &node_secret)) }) .map_err(|_| anyhow!("Failed to sign invoice"))?; - let invoice = Invoice::from_signed(signed_invoice)?; + let invoice = Bolt11Invoice::from_signed(signed_invoice)?; Ok(invoice) } @@ -222,7 +222,7 @@ where Ok(route_hint_hop) } - pub fn pay_invoice(&self, invoice: &Invoice, amount: Option) -> Result<()> { + pub fn pay_invoice(&self, invoice: &Bolt11Invoice, amount: Option) -> Result<()> { let (result, amt_msat) = match invoice.amount_milli_satoshis() { Some(_) => { let result = pay_invoice(invoice, Retry::Attempts(10), &self.channel_manager); @@ -270,8 +270,8 @@ where }; let description = match invoice.description() { - InvoiceDescription::Direct(des) => des.clone().into_inner(), - InvoiceDescription::Hash(lightning_invoice::Sha256(des)) => des.to_string(), + Bolt11InvoiceDescription::Direct(des) => des.clone().into_inner(), + Bolt11InvoiceDescription::Hash(lightning_invoice::Sha256(des)) => des.to_string(), }; self.storage.insert_payment( diff --git a/crates/ln-dlc-node/src/node/mod.rs b/crates/ln-dlc-node/src/node/mod.rs index f37457127..dec1f1955 100644 --- a/crates/ln-dlc-node/src/node/mod.rs +++ b/crates/ln-dlc-node/src/node/mod.rs @@ -1,5 +1,3 @@ -pub use self::dlc_manager::signed_channel_state_name; -pub use self::dlc_manager::DlcManager; use crate::channel::UserChannelId; use crate::disk; use crate::dlc_custom_signer::CustomKeysManager; @@ -8,7 +6,6 @@ use crate::ln::manage_spendable_outputs; use crate::ln::TracingLogger; use crate::ln_dlc_wallet::LnDlcWallet; use crate::node::dlc_channel::sub_channel_manager_periodic_check; -pub use crate::node::oracle::OracleInfo; use crate::node::peer_manager::alias_as_bytes; use crate::node::peer_manager::broadcast_node_announcement; use crate::on_chain_wallet::OnChainWallet; @@ -18,31 +15,28 @@ use crate::ChainMonitor; use crate::EventHandlerTrait; use crate::NetworkGraph; use crate::PeerManager; -pub use ::dlc_manager as rust_dlc_manager; use anyhow::Context; use anyhow::Result; use bitcoin::hashes::hex::ToHex; use bitcoin::secp256k1::PublicKey; use bitcoin::Network; use bitcoin::Txid; -pub use channel_manager::ChannelManager; -pub use dlc_channel::dlc_message_name; -pub use dlc_channel::sub_channel_message_name; use dlc_messages::message_handler::MessageHandler as DlcMessageHandler; use dlc_sled_storage_provider::SledStorageProvider; use futures::future::RemoteHandle; use futures::FutureExt; -pub use invoice::HTLCStatus; use lightning::chain::chainmonitor; -use lightning::chain::keysinterface::EntropySource; -use lightning::chain::keysinterface::KeysManager; use lightning::chain::Confirm; use lightning::ln::msgs::NetAddress; +use lightning::ln::peer_handler::IgnoringMessageHandler; use lightning::ln::peer_handler::MessageHandler; use lightning::routing::gossip::P2PGossipSync; use lightning::routing::router::DefaultRouter; use lightning::routing::scoring::ProbabilisticScorer; +use lightning::routing::scoring::ProbabilisticScoringFeeParameters; use lightning::routing::utxo::UtxoLookup; +use lightning::sign::EntropySource; +use lightning::sign::KeysManager; use lightning::util::config::UserConfig; use lightning_background_processor::process_events_async; use lightning_background_processor::GossipSync; @@ -64,12 +58,8 @@ use std::sync::Mutex; use std::time::Duration; use std::time::Instant; use std::time::SystemTime; -pub use storage::InMemoryStore; -pub use storage::Storage; -pub use sub_channel_manager::SubChannelManager; use tokio::sync::RwLock; use tokio::task::spawn_blocking; -pub use wallet::PaymentDetails; mod channel_manager; mod connection; @@ -85,6 +75,20 @@ pub(crate) mod invoice; pub mod peer_manager; +pub use crate::node::dlc_manager::signed_channel_state_name; +pub use crate::node::dlc_manager::DlcManager; +pub use crate::node::oracle::OracleInfo; +pub use ::dlc_manager as rust_dlc_manager; +pub use channel_manager::ChannelManager; +pub use dlc_channel::dlc_message_name; +pub use dlc_channel::send_dlc_message; +pub use dlc_channel::sub_channel_message_name; +pub use invoice::HTLCStatus; +pub use storage::InMemoryStore; +pub use storage::Storage; +pub use sub_channel_manager::SubChannelManager; +pub use wallet::PaymentDetails; + /// The interval at which the [`lightning::ln::msgs::NodeAnnouncement`] is broadcast. /// /// According to the LDK team, a value of up to 1 hour should be fine. @@ -324,11 +328,13 @@ where logger.clone(), ))); + let scoring_fee_params = ProbabilisticScoringFeeParameters::default(); let router = Arc::new(DefaultRouter::new( network_graph.clone(), logger.clone(), keys_manager.get_secure_random_bytes(), scorer.clone(), + scoring_fee_params, )); let channel_manager = channel_manager::build( @@ -364,17 +370,20 @@ where )?; let dlc_manager = Arc::new(dlc_manager); - let sub_channel_manager = - sub_channel_manager::build(channel_manager.clone(), dlc_manager.clone())?; + let sub_channel_manager = sub_channel_manager::build( + channel_manager.clone(), + dlc_manager.clone(), + chain_monitor.clone(), + keys_manager.clone(), + )?; let dlc_message_handler = Arc::new(DlcMessageHandler::new()); let lightning_msg_handler = MessageHandler { chan_handler: sub_channel_manager.clone(), route_handler: gossip_sync.clone(), - // Hooking the dlc_message_handler here to intercept the `peer_disconnected` event and - // clear all pending unprocessed message from the disconnected peer. - onion_message_handler: dlc_message_handler.clone(), + onion_message_handler: Arc::new(IgnoringMessageHandler {}), + custom_message_handler: dlc_message_handler.clone(), }; let peer_manager: Arc = Arc::new(PeerManager::new( @@ -382,7 +391,6 @@ where time_since_unix_epoch.as_secs() as u32, &ephemeral_randomness, logger.clone(), - dlc_message_handler.clone(), keys_manager.clone(), )); @@ -427,7 +435,11 @@ where /// Starts the background handles - if the returned handles are dropped, the /// background tasks are stopped. // TODO: Consider having handles for *all* the tasks & threads for a clean shutdown. - pub fn start(&self, event_handler: impl EventHandlerTrait + 'static) -> Result { + pub fn start( + &self, + event_handler: impl EventHandlerTrait + 'static, + mobile_interruptable_platform: bool, + ) -> Result { let mut handles = vec![spawn_connection_management( self.peer_manager.clone(), self.listen_address, @@ -461,6 +473,7 @@ where event_handler, self.gossip_sync.clone(), self.scorer.clone(), + mobile_interruptable_platform, )); handles.push(spawn_broadcast_node_annoucements( @@ -473,6 +486,12 @@ where handles.push(manage_sub_channels( self.sub_channel_manager.clone(), self.dlc_message_handler.clone(), + self.peer_manager.clone(), + self.settings.clone(), + )); + + handles.push(manage_dlc_manager( + self.dlc_manager.clone(), self.settings.clone(), )); @@ -516,6 +535,7 @@ where sub_channel_manager_periodic_check( self.sub_channel_manager.clone(), &self.dlc_message_handler, + &self.peer_manager, ) .await } @@ -591,6 +611,7 @@ fn spawn_background_processor( event_handler: impl EventHandlerTrait + 'static, gossip_sync: Arc, scorer: Arc>, + mobile_interruptable_platform: bool, ) -> RemoteHandle<()> { tracing::info!("Starting background processor"); let (fut, remote_handle) = async move { @@ -609,6 +630,7 @@ fn spawn_background_processor( false }) }, + mobile_interruptable_platform, ) .await { @@ -799,6 +821,7 @@ async fn manage_spendable_outputs_task( fn manage_sub_channels( sub_channel_manager: Arc, dlc_message_handler: Arc, + peer_manager: Arc, settings: Arc>, ) -> RemoteHandle<()> { let (fut, remote_handle) = { @@ -809,6 +832,7 @@ fn manage_sub_channels( if let Err(e) = sub_channel_manager_periodic_check( sub_channel_manager.clone(), &dlc_message_handler, + &peer_manager, ) .await { @@ -835,6 +859,43 @@ fn manage_sub_channels( remote_handle } +/// Spawn a task that manages dlc manager +fn manage_dlc_manager( + dlc_manager: Arc, + settings: Arc>, +) -> RemoteHandle<()> { + let (fut, remote_handle) = { + async move { + loop { + tracing::trace!("Started periodic dlc manager check"); + let now = Instant::now(); + if let Err(e) = dlc_manager.periodic_chain_monitor() { + tracing::error!("Failed to perform periodic chain monitor check: {e:#}"); + }; + if let Err(e) = dlc_manager.periodic_check() { + tracing::error!("Failed to perform periodic check: {e:#}"); + }; + + tracing::trace!( + duration = now.elapsed().as_millis(), + "Finished periodic check" + ); + + let interval = { + let guard = settings.read().await; + guard.dlc_manager_periodic_check_interval + }; + tokio::time::sleep(interval).await; + } + } + } + .remote_handle(); + + tokio::spawn(fut); + + remote_handle +} + impl Display for NodeInfo { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { format!("{}@{}", self.pubkey, self.address).fmt(f) diff --git a/crates/ln-dlc-node/src/node/storage.rs b/crates/ln-dlc-node/src/node/storage.rs index 02f7694e3..3dd11e76c 100644 --- a/crates/ln-dlc-node/src/node/storage.rs +++ b/crates/ln-dlc-node/src/node/storage.rs @@ -7,13 +7,13 @@ use crate::PaymentFlow; use crate::PaymentInfo; use anyhow::Result; use bitcoin::secp256k1::PublicKey; -use lightning::chain::keysinterface::DelayedPaymentOutputDescriptor; -use lightning::chain::keysinterface::SpendableOutputDescriptor; -use lightning::chain::keysinterface::StaticPaymentOutputDescriptor; use lightning::chain::transaction::OutPoint; use lightning::ln::PaymentHash; use lightning::ln::PaymentPreimage; use lightning::ln::PaymentSecret; +use lightning::sign::DelayedPaymentOutputDescriptor; +use lightning::sign::SpendableOutputDescriptor; +use lightning::sign::StaticPaymentOutputDescriptor; use parking_lot::Mutex; use std::collections::HashMap; use std::sync::Arc; diff --git a/crates/ln-dlc-node/src/node/sub_channel_manager.rs b/crates/ln-dlc-node/src/node/sub_channel_manager.rs index 648e2fc4b..ef42459b4 100644 --- a/crates/ln-dlc-node/src/node/sub_channel_manager.rs +++ b/crates/ln-dlc-node/src/node/sub_channel_manager.rs @@ -1,7 +1,9 @@ +use crate::dlc_custom_signer::CustomKeysManager; use crate::fee_rate_estimator::FeeRateEstimator; use crate::ln_dlc_wallet::LnDlcWallet; use crate::node::channel_manager::ChannelManager; use crate::node::dlc_manager::DlcManager; +use crate::ChainMonitor; use crate::CustomSigner; use anyhow::Result; use dlc_manager::sub_channel_manager; @@ -13,6 +15,7 @@ use std::sync::Arc; pub type SubChannelManager = sub_channel_manager::SubChannelManager< Arc, Arc, + Arc, Arc, Arc, Arc, @@ -20,14 +23,20 @@ pub type SubChannelManager = sub_channel_manager::SubChannelManager< Arc, Arc, CustomSigner, + Arc, + CustomSigner, >; pub(crate) fn build( channel_manager: Arc, dlc_manager: Arc, + monitor: Arc, + provider: Arc, ) -> Result> { Ok(Arc::new(SubChannelManager::new( - channel_manager.clone(), + channel_manager, dlc_manager, + monitor, + provider, )?)) } diff --git a/crates/ln-dlc-node/src/scorer.rs b/crates/ln-dlc-node/src/scorer.rs index 32679be4a..7f21cc7d5 100644 --- a/crates/ln-dlc-node/src/scorer.rs +++ b/crates/ln-dlc-node/src/scorer.rs @@ -1,7 +1,7 @@ use crate::ln::TracingLogger; use crate::NetworkGraph; use lightning::routing::scoring::ProbabilisticScorer; -use lightning::routing::scoring::ProbabilisticScoringParameters; +use lightning::routing::scoring::ProbabilisticScoringDecayParameters; use lightning::util::ser::ReadableArgs; use std::fs::File; use std::io::BufReader; @@ -14,9 +14,9 @@ pub fn persistent_scorer( graph: Arc, logger: Arc, ) -> ProbabilisticScorer, Arc> { - let params = ProbabilisticScoringParameters::default(); + let params = ProbabilisticScoringDecayParameters::default(); if let Ok(file) = File::open(path) { - let args = (params.clone(), graph.clone(), logger.clone()); + let args = (params, graph.clone(), logger.clone()); match ProbabilisticScorer::read(&mut BufReader::new(file), args) { Ok(scorer) => return scorer, Err(e) => tracing::error!("Failed to read scorer from disk: {e}"), @@ -31,6 +31,6 @@ pub fn in_memory_scorer( graph: Arc, logger: Arc, ) -> ProbabilisticScorer, Arc> { - let params = ProbabilisticScoringParameters::default(); + let params = ProbabilisticScoringDecayParameters::default(); ProbabilisticScorer::new(params, graph, logger) } diff --git a/crates/ln-dlc-node/src/tests/dlc/create.rs b/crates/ln-dlc-node/src/tests/dlc/create.rs index 949088711..60722cb6a 100644 --- a/crates/ln-dlc-node/src/tests/dlc/create.rs +++ b/crates/ln-dlc-node/src/tests/dlc/create.rs @@ -2,6 +2,7 @@ use crate::node::InMemoryStore; use crate::node::Node; use crate::tests::dummy_contract_input; use crate::tests::init_tracing; +use crate::tests::wait_for_n_usable_channels; use crate::tests::wait_until_dlc_channel_state; use crate::tests::SubChannelStateName; use anyhow::Context; @@ -39,8 +40,6 @@ async fn given_lightning_channel_then_can_add_dlc_channel() { .await .unwrap(); - tokio::time::sleep(Duration::from_secs(5)).await; - // Act and assert create_dlc_channel( @@ -54,44 +53,46 @@ async fn given_lightning_channel_then_can_add_dlc_channel() { } pub async fn create_dlc_channel( - app: &Node, - coordinator: &Node, + offer_node: &Node, + accept_node: &Node, app_dlc_collateral: u64, coordinator_dlc_collateral: u64, ) -> Result<()> { // Act - let oracle_pk = app.oracle_pk(); + let oracle_pk = offer_node.oracle_pk(); let contract_input = dummy_contract_input(app_dlc_collateral, coordinator_dlc_collateral, oracle_pk); - let channel_details = app + wait_for_n_usable_channels(1, offer_node).await?; + let channel_details = offer_node .channel_manager .list_usable_channels() .iter() - .find(|c| c.counterparty.node_id == coordinator.info.pubkey) + .find(|c| c.counterparty.node_id == accept_node.info.pubkey) .context("Could not find usable channel with peer")? .clone(); - app.propose_dlc_channel(channel_details.clone(), contract_input) + offer_node + .propose_dlc_channel(channel_details.clone(), contract_input) .await?; // Process the app's `Offer` let sub_channel = wait_until_dlc_channel_state( Duration::from_secs(30), - coordinator, - app.info.pubkey, + accept_node, + offer_node.info.pubkey, SubChannelStateName::Offered, ) .await?; - coordinator.accept_dlc_channel_offer(&sub_channel.channel_id)?; + accept_node.accept_dlc_channel_offer(&sub_channel.channel_id)?; // Process the coordinator's `Accept` and send `Confirm` wait_until_dlc_channel_state( Duration::from_secs(30), - app, - coordinator.info.pubkey, + offer_node, + accept_node.info.pubkey, SubChannelStateName::Confirmed, ) .await?; @@ -99,8 +100,8 @@ pub async fn create_dlc_channel( // Process the app's `Confirm` and send `Finalize` wait_until_dlc_channel_state( Duration::from_secs(30), - coordinator, - app.info.pubkey, + accept_node, + offer_node.info.pubkey, SubChannelStateName::Finalized, ) .await?; @@ -110,8 +111,8 @@ pub async fn create_dlc_channel( // Process the coordinator's `Finalize` and send `Revoke` wait_until_dlc_channel_state( Duration::from_secs(30), - app, - coordinator.info.pubkey, + offer_node, + accept_node.info.pubkey, SubChannelStateName::Signed, ) .await?; @@ -119,8 +120,8 @@ pub async fn create_dlc_channel( // Process the app's `Revoke` wait_until_dlc_channel_state( Duration::from_secs(30), - coordinator, - app.info.pubkey, + accept_node, + offer_node.info.pubkey, SubChannelStateName::Signed, ) .await?; diff --git a/crates/ln-dlc-node/src/tests/dlc/dlc_setup_with_reconnects.rs b/crates/ln-dlc-node/src/tests/dlc/dlc_setup_with_reconnects.rs index 840b92ed9..8ef3c321a 100644 --- a/crates/ln-dlc-node/src/tests/dlc/dlc_setup_with_reconnects.rs +++ b/crates/ln-dlc-node/src/tests/dlc/dlc_setup_with_reconnects.rs @@ -3,6 +3,8 @@ use crate::node::Node; use crate::tests::dlc::create::create_dlc_channel; use crate::tests::dummy_contract_input; use crate::tests::init_tracing; +use crate::tests::wait_for_n_usable_channels; +use crate::tests::wait_until; use crate::tests::wait_until_dlc_channel_state; use crate::tests::SubChannelStateName; use anyhow::Context; @@ -34,8 +36,11 @@ async fn reconnecting_during_dlc_channel_setup() { .open_private_channel(&app, 50_000, 50_000) .await .unwrap(); - let channel_details = app.channel_manager.list_usable_channels(); - let channel_details = channel_details + + wait_for_n_usable_channels(1, &coordinator).await.unwrap(); + let channel_details = app + .channel_manager + .list_usable_channels() .iter() .find(|c| c.counterparty.node_id == coordinator_info.pubkey) .context("No usable channels for app") @@ -51,7 +56,7 @@ async fn reconnecting_during_dlc_channel_setup() { .await .unwrap(); - // Process the app's `Offer` + // Process the app's `Offer`. let sub_channel = wait_until_dlc_channel_state( Duration::from_secs(30), &coordinator, @@ -62,11 +67,16 @@ async fn reconnecting_during_dlc_channel_setup() { .unwrap(); app.disconnect(coordinator.info); - // we need to wait a few seconds for the disconnect to get updated to the channel_state. - tokio::time::sleep(Duration::from_secs(5)).await; - // assert that the accept dlc_channel_offer fails if the peers are disconnected (do not - // panic as in https://github.com/get10101/10101/issues/760). + // We need to wait for the channel to not be usable after the disconnect. + wait_until(Duration::from_secs(5), || async { + Ok(coordinator.list_usable_channels().is_empty().then_some(())) + }) + .await + .unwrap(); + + // Assert that `accept_dlc_channel_offer` fails if the peer is disconnected (do not panic as in + // https://github.com/get10101/10101/issues/760). assert!(coordinator .accept_dlc_channel_offer(&sub_channel.channel_id) .is_err()); @@ -77,18 +87,6 @@ async fn reconnecting_during_dlc_channel_setup() { .accept_dlc_channel_offer(&sub_channel.channel_id) .unwrap(); - // This is the point of this test: to verify that reconnecting during DLC channel setup can be - // fixed by processing pending DLC actions - app.reconnect(coordinator.info).await.unwrap(); - - // Instruct coordinator to re-send the accept message - sub_channel_manager_periodic_check( - coordinator.sub_channel_manager.clone(), - &coordinator.dlc_message_handler, - ) - .await - .unwrap(); - // Process the coordinator's `Accept` and send `Confirm` wait_until_dlc_channel_state( Duration::from_secs(30), @@ -99,31 +97,44 @@ async fn reconnecting_during_dlc_channel_setup() { .await .unwrap(); - // Wait for the `Confirm` message to be delivered - tokio::time::sleep(Duration::from_secs(2)).await; + // Wait for the `Confirm` message to be delivered. + wait_until(Duration::from_secs(5), || async { + Ok(coordinator + .dlc_message_handler + .has_pending_messages_to_process() + .then_some(())) + }) + .await + .unwrap(); app.reconnect(coordinator_info).await.unwrap(); - // Wait for the peers to reconnect and get the `ChannelReestablish` event. During the reconnect - // the coordinator will return from `Accepted` to the `Offered` state. - tokio::time::sleep(Duration::from_secs(2)).await; + // Process the app's first `Confirm` message. This should fail because of an invalid commitment + // transaction number. + coordinator + .process_incoming_messages() + .expect_err("should have invalid commitment transaction number"); - // The coordinator handles `ReAccept` action. We need this so that the coordinator advances its - // state to `Accepted` again, so that it can process the app's repeated `Confirm` message + // Create second `Accept` message from pending `ReAccept` action. sub_channel_manager_periodic_check( coordinator.sub_channel_manager.clone(), &coordinator.dlc_message_handler, + &coordinator.peer_manager, ) .await .unwrap(); - // Wait for the `Accepted` message to be processed - tokio::time::sleep(Duration::from_secs(2)).await; - // The app processes the repeated accept message from the coordinator and sends another confirm - // message. - app.process_incoming_messages().unwrap(); + // Process the coordinator's second `Accept` and send `Confirm`. + wait_until_dlc_channel_state( + Duration::from_secs(30), + &app, + coordinator.info.pubkey, + SubChannelStateName::Confirmed, + ) + .await + .unwrap(); - // Process the resent `Confirm` message from the App. + // Process the app's second `Confirm` message and send `Finalize`. wait_until_dlc_channel_state( Duration::from_secs(30), &coordinator, @@ -133,12 +144,7 @@ async fn reconnecting_during_dlc_channel_setup() { .await .unwrap(); - assert!(app - .list_channels() - .iter() - .any(|channel| channel.channel_id == channel_details.channel_id)); - - // Process the `Finalize` message from the Coordinator. + // Process the coordinator's `Finalize` message and send `Revoke`. wait_until_dlc_channel_state( Duration::from_secs(30), &app, @@ -148,6 +154,16 @@ async fn reconnecting_during_dlc_channel_setup() { .await .unwrap(); + // Process the app's `Revoke` message. + wait_until_dlc_channel_state( + Duration::from_secs(30), + &coordinator, + app.info.pubkey, + SubChannelStateName::Signed, + ) + .await + .unwrap(); + let coordinator_settlement_amount = 12_500; app.propose_dlc_channel_collaborative_settlement( channel_details.channel_id, @@ -156,7 +172,7 @@ async fn reconnecting_during_dlc_channel_setup() { .await .unwrap(); - // Process the app's `CloseOffer` + // Process the app's `CloseOffer`. let sub_channel = wait_until_dlc_channel_state( Duration::from_secs(30), &coordinator, @@ -170,7 +186,7 @@ async fn reconnecting_during_dlc_channel_setup() { .accept_dlc_channel_collaborative_settlement(&sub_channel.channel_id) .unwrap(); - // Process the coordinator's `CloseAccept` and send `CloseConfirm` + // Process the coordinator's `CloseAccept` and send `CloseConfirm`. wait_until_dlc_channel_state( Duration::from_secs(30), &app, @@ -180,32 +196,44 @@ async fn reconnecting_during_dlc_channel_setup() { .await .unwrap(); - // Wait for the `CloseConfirm` message to be delivered - tokio::time::sleep(Duration::from_secs(2)).await; + // Wait for the `CloseConfirm` message to be delivered. + wait_until(Duration::from_secs(5), || async { + Ok(coordinator + .dlc_message_handler + .has_pending_messages_to_process() + .then_some(())) + }) + .await + .unwrap(); app.reconnect(coordinator_info).await.unwrap(); - // Wait for the peers to reconnect and get the `ChannelReestablish` event. During the reconnect - // the coordinator will return from `CloseAccepted` to the `Signed` state. - tokio::time::sleep(Duration::from_secs(2)).await; + // Process the app's first `CloseConfirm` message. This should fail because of an invalid + // commitment transaction number. + coordinator + .process_incoming_messages() + .expect_err("should have invalid commitment transaction number"); - // The coordinator handles `ReCloseAccept` action. We need this so that the coordinator advances - // its state to `CloseAccepted` again, so that it can process the app's repeated - // `CloseConfirm` message + // Create second `CloseAccept` message from pending `ReAcceptCloseOffer` action. sub_channel_manager_periodic_check( coordinator.sub_channel_manager.clone(), &coordinator.dlc_message_handler, + &coordinator.peer_manager, ) .await .unwrap(); - // Wait for the `CloseAccepted` message to be processed - tokio::time::sleep(Duration::from_secs(2)).await; - // The app processes the repeated `CloseAccept` message from the coordinator and sends another - // `CloseConfirm` message. - app.process_incoming_messages().unwrap(); + // Process the coordinator's second `CloseAccept` and send `CloseConfirm`. + wait_until_dlc_channel_state( + Duration::from_secs(30), + &app, + coordinator.info.pubkey, + SubChannelStateName::CloseConfirmed, + ) + .await + .unwrap(); - // Process the app's `CloseConfirm` and send `CloseFinalize` + // Process the app's `CloseConfirm` and send `CloseFinalize`. wait_until_dlc_channel_state( Duration::from_secs(30), &coordinator, @@ -215,7 +243,7 @@ async fn reconnecting_during_dlc_channel_setup() { .await .unwrap(); - // Process the coordinator's `CloseFinalize` + // Process the coordinator's `CloseFinalize`. wait_until_dlc_channel_state( Duration::from_secs(30), &app, @@ -256,8 +284,6 @@ async fn can_lose_connection_before_processing_subchannel_close_finalize() { .await .unwrap(); - tokio::time::sleep(Duration::from_secs(5)).await; - create_dlc_channel( &coordinator, &app, @@ -318,7 +344,14 @@ async fn can_lose_connection_before_processing_subchannel_close_finalize() { .await .unwrap(); - tokio::time::sleep(Duration::from_secs(5)).await; + wait_until(Duration::from_secs(5), || async { + Ok(coordinator + .dlc_message_handler + .has_pending_messages_to_process() + .then_some(())) + }) + .await + .unwrap(); app.reconnect(coordinator.info).await.unwrap(); @@ -339,138 +372,6 @@ async fn can_lose_connection_before_processing_subchannel_close_finalize() { assert!(matches!(state, SubChannelState::OffChainClosed)); } -#[tokio::test(flavor = "multi_thread")] -#[ignore] -async fn reconnecting_during_dlc_channel_setup_reversed() { - init_tracing(); - - // Arrange - - let (app, _running_app) = Node::start_test_app("app").unwrap(); - let (coordinator, _running_coord) = Node::start_test_coordinator("coordinator").unwrap(); - - let coordinator_info = coordinator.info; - - app.connect(coordinator_info).await.unwrap(); - - coordinator - .fund(Amount::from_sat(10_000_000)) - .await - .unwrap(); - - coordinator - .open_private_channel(&app, 50_000, 50_000) - .await - .unwrap(); - - let channel_details = app.channel_manager.list_usable_channels(); - let channel_details = channel_details - .iter() - .find(|c| c.counterparty.node_id == coordinator.info.pubkey) - .context("No usable channels for app") - .unwrap() - .clone(); - - // Act - - let oracle_pk = app.oracle_pk(); - let contract_input = dummy_contract_input(20_000, 20_000, oracle_pk); - - app.propose_dlc_channel(channel_details.clone(), contract_input) - .await - .unwrap(); - - // Process the coordinator's `Offer` - let sub_channel = wait_until_dlc_channel_state( - Duration::from_secs(30), - &coordinator, - app.info.pubkey, - SubChannelStateName::Offered, - ) - .await - .unwrap(); - - coordinator - .accept_dlc_channel_offer(&sub_channel.channel_id) - .unwrap(); - - // Instruct app to re-send the accept message - sub_channel_manager_periodic_check(app.sub_channel_manager.clone(), &app.dlc_message_handler) - .await - .unwrap(); - - // Process the coordinator's `Accept` and send `Confirm` - wait_until_dlc_channel_state( - Duration::from_secs(30), - &app, - coordinator.info.pubkey, - SubChannelStateName::Confirmed, - ) - .await - .unwrap(); - - // Wait for the `Confirm` message to be delivered - tokio::time::sleep(Duration::from_secs(2)).await; - - // -----> Error happens on that reconnect! - app.reconnect(coordinator_info).await.unwrap(); - - // Wait for the peers to reconnect and get the `ChannelReestablish` event. During the reconnect - // the app will return from `Accepted` to the `Offered` state. - tokio::time::sleep(Duration::from_secs(10)).await; - - // Process the coordinator's `Accept` and send `Confirm` - wait_until_dlc_channel_state( - Duration::from_secs(30), - &app, - coordinator.info.pubkey, - SubChannelStateName::Confirmed, - ) - .await - .unwrap(); - - // Process the app's `Confirm` and send `Finalize` - wait_until_dlc_channel_state( - Duration::from_secs(30), - &coordinator, - app.info.pubkey, - SubChannelStateName::Finalized, - ) - .await - .unwrap(); - - // Wait for the `Accepted` message to be processed - tokio::time::sleep(Duration::from_secs(2)).await; - // The coordinator processes the repeated accept message from the coordinator and sends another - // confirm message. - coordinator.process_incoming_messages().unwrap(); - - // Process the resend `Confirm` message from the Coordinator. - wait_until_dlc_channel_state( - Duration::from_secs(120), - &app, - coordinator.info.pubkey, - SubChannelStateName::Signed, - ) - .await - .unwrap(); - - assert!(coordinator - .list_channels() - .iter() - .any(|channel| channel.channel_id == channel_details.channel_id)); - - // Process the `Finalize` message from the App. - wait_until_dlc_channel_state( - Duration::from_secs(30), - &coordinator, - app.info.pubkey, - SubChannelStateName::Signed, - ) - .await - .unwrap(); -} - #[tokio::test(flavor = "multi_thread")] #[ignore] async fn can_lose_connection_before_processing_subchannel_accept() { @@ -501,12 +402,11 @@ async fn can_lose_connection_before_processing_subchannel_accept() { .await .unwrap(); - tokio::time::sleep(Duration::from_secs(5)).await; - let oracle_pk = app.oracle_pk(); let contract_input = dummy_contract_input(coordinator_dlc_collateral, app_dlc_collateral, oracle_pk); + wait_for_n_usable_channels(1, &coordinator).await.unwrap(); let channel_details = coordinator .channel_manager .list_usable_channels() @@ -521,7 +421,7 @@ async fn can_lose_connection_before_processing_subchannel_accept() { .await .unwrap(); - // Process the coordinator's `Offer` + // Process the coordinator's `Offer`. let sub_channel = wait_until_dlc_channel_state( Duration::from_secs(30), &app, @@ -534,28 +434,35 @@ async fn can_lose_connection_before_processing_subchannel_accept() { app.accept_dlc_channel_offer(&sub_channel.channel_id) .unwrap(); - // Give time to deliver the `Accept` message to the coordinator - tokio::time::sleep(Duration::from_secs(5)).await; + // Give time to deliver the `Accept` message to the coordinator. + wait_until(Duration::from_secs(5), || async { + Ok(coordinator + .dlc_message_handler + .has_pending_messages_to_process() + .then_some(())) + }) + .await + .unwrap(); // Lose the connection, triggering the coordinator's rollback to the `Offered` state. app.reconnect(coordinator.info).await.unwrap(); - // Process the app's `Accept` and send `Confirm` - wait_until_dlc_channel_state( - Duration::from_secs(30), - &coordinator, - app.info.pubkey, - SubChannelStateName::Confirmed, + // Process the app's first `Accept` message. This should fail because of an invalid commitment + // transaction number. + coordinator + .process_incoming_messages() + .expect_err("should have invalid commitment transaction number"); + + // Create second `Accept` message from pending `ReAccept` action. + sub_channel_manager_periodic_check( + app.sub_channel_manager.clone(), + &app.dlc_message_handler, + &app.peer_manager, ) .await .unwrap(); - // Create `Accept` message from pending `ReAccept` Action - sub_channel_manager_periodic_check(app.sub_channel_manager.clone(), &app.dlc_message_handler) - .await - .unwrap(); - - // Process the app's `Accept` and send `Confirm` + // Process the app's second `Accept` and send `Confirm`. wait_until_dlc_channel_state( Duration::from_secs(30), &coordinator, @@ -565,7 +472,7 @@ async fn can_lose_connection_before_processing_subchannel_accept() { .await .unwrap(); - // Process the coordinator's `Confirm` and send `Finalize` + // Process the coordinator's `Confirm` and send `Finalize`. wait_until_dlc_channel_state( Duration::from_secs(30), &app, @@ -575,7 +482,7 @@ async fn can_lose_connection_before_processing_subchannel_accept() { .await .unwrap(); - // Process the app's `Finalize` and send `Revoke` + // Process the app's `Finalize` and send `Revoke`. wait_until_dlc_channel_state( Duration::from_secs(30), &coordinator, @@ -585,7 +492,7 @@ async fn can_lose_connection_before_processing_subchannel_accept() { .await .unwrap(); - // Process the coordinator's `Revoke` + // Process the coordinator's `Revoke`. wait_until_dlc_channel_state( Duration::from_secs(30), &app, @@ -604,21 +511,27 @@ async fn can_lose_connection_before_processing_subchannel_close_accept() { let app_dlc_collateral = 25_000; let coordinator_dlc_collateral = 50_000; + let app_ln_balance = app_dlc_collateral * 2; let coordinator_ln_balance = coordinator_dlc_collateral * 2; + let fund_amount = (app_ln_balance + coordinator_ln_balance) * 2; + let (app, _running_app) = Node::start_test_app("app").unwrap(); let (coordinator, _running_coord) = Node::start_test_coordinator("coordinator").unwrap(); + app.connect(coordinator.info).await.unwrap(); + coordinator .fund(Amount::from_sat(fund_amount)) .await .unwrap(); + coordinator .open_private_channel(&app, coordinator_ln_balance, app_ln_balance) .await .unwrap(); - tokio::time::sleep(Duration::from_secs(5)).await; + create_dlc_channel( &coordinator, &app, @@ -627,6 +540,7 @@ async fn can_lose_connection_before_processing_subchannel_close_accept() { ) .await .unwrap(); + let channel_details = coordinator .channel_manager .list_usable_channels() @@ -643,7 +557,7 @@ async fn can_lose_connection_before_processing_subchannel_close_accept() { .await .unwrap(); - // Process `CloseOffer` + // Process `CloseOffer`. let sub_channel = wait_until_dlc_channel_state( Duration::from_secs(30), &app, @@ -652,21 +566,39 @@ async fn can_lose_connection_before_processing_subchannel_close_accept() { ) .await .unwrap(); + app.accept_dlc_channel_collaborative_settlement(&sub_channel.channel_id) .unwrap(); - // Give time to deliver the `CloseAccept` message to the coordinator - tokio::time::sleep(Duration::from_secs(5)).await; + // Give time to deliver the `CloseAccept` message to the coordinator. + wait_until(Duration::from_secs(5), || async { + Ok(coordinator + .dlc_message_handler + .has_pending_messages_to_process() + .then_some(())) + }) + .await + .unwrap(); - // Lose the connection, triggering re-establishing the channel + // Lose the connection, triggering re-establishing the channel. app.reconnect(coordinator.info).await.unwrap(); - // Create `CloseAccept` message from pending `ReCloseAccept` Action - sub_channel_manager_periodic_check(app.sub_channel_manager.clone(), &app.dlc_message_handler) - .await - .unwrap(); + // Process the app's first `CloseAccept` message. This should fail because of an invalid + // commitment transaction number. + coordinator + .process_incoming_messages() + .expect_err("should have invalid commitment transaction number"); - // Process `CloseAccept` and send `CloseConfirm` + // Create second `CloseAccept` message from pending `ReCloseAccept` action. + sub_channel_manager_periodic_check( + app.sub_channel_manager.clone(), + &app.dlc_message_handler, + &app.peer_manager, + ) + .await + .unwrap(); + + // Process second `CloseAccept` and send `CloseConfirm`. wait_until_dlc_channel_state( Duration::from_secs(30), &coordinator, @@ -675,4 +607,24 @@ async fn can_lose_connection_before_processing_subchannel_close_accept() { ) .await .unwrap(); + + // Process the coordinator's `CloseConfirm` and send `CloseFinalize` + wait_until_dlc_channel_state( + Duration::from_secs(30), + &app, + coordinator.info.pubkey, + SubChannelStateName::OffChainClosed, + ) + .await + .unwrap(); + + // Process the coordinator's `CloseFinalize` + wait_until_dlc_channel_state( + Duration::from_secs(30), + &coordinator, + app.info.pubkey, + SubChannelStateName::OffChainClosed, + ) + .await + .unwrap(); } diff --git a/crates/ln-dlc-node/src/tests/dlc/non_collaborative_settlement.rs b/crates/ln-dlc-node/src/tests/dlc/non_collaborative_settlement.rs index 2ca218715..fda7437c5 100644 --- a/crates/ln-dlc-node/src/tests/dlc/non_collaborative_settlement.rs +++ b/crates/ln-dlc-node/src/tests/dlc/non_collaborative_settlement.rs @@ -63,6 +63,7 @@ async fn force_close_ln_dlc_channel() { sub_channel_manager_periodic_check( coordinator.sub_channel_manager.clone(), &coordinator.dlc_message_handler, + &coordinator.peer_manager, ) .await .unwrap(); @@ -85,6 +86,7 @@ async fn force_close_ln_dlc_channel() { sub_channel_manager_periodic_check( coordinator.sub_channel_manager.clone(), &coordinator.dlc_message_handler, + &coordinator.peer_manager, ) .await .unwrap(); diff --git a/crates/ln-dlc-node/src/tests/just_in_time_channel/fail_intercepted_htlc.rs b/crates/ln-dlc-node/src/tests/just_in_time_channel/fail_intercepted_htlc.rs index da6760dd7..9c2e5d000 100644 --- a/crates/ln-dlc-node/src/tests/just_in_time_channel/fail_intercepted_htlc.rs +++ b/crates/ln-dlc-node/src/tests/just_in_time_channel/fail_intercepted_htlc.rs @@ -8,7 +8,7 @@ use crate::tests::init_tracing; use crate::tests::setup_coordinator_payer_channel; use crate::HTLCStatus; use bitcoin::Amount; -use lightning::util::events::Event; +use lightning::events::Event; use std::ops::Add; use std::sync::Arc; use std::time::Duration; diff --git a/crates/ln-dlc-node/src/tests/just_in_time_channel/payments.rs b/crates/ln-dlc-node/src/tests/just_in_time_channel/payments.rs index 453829307..bd39a01c5 100644 --- a/crates/ln-dlc-node/src/tests/just_in_time_channel/payments.rs +++ b/crates/ln-dlc-node/src/tests/just_in_time_channel/payments.rs @@ -22,7 +22,7 @@ async fn just_in_time_channel_with_multiple_payments() { payer.connect(coordinator.info).await.unwrap(); payee.connect(coordinator.info).await.unwrap(); - let payer_to_payee_invoice_amount = 5_000; + let payer_to_payee_invoice_amount = 25_000; let expected_coordinator_payee_channel_value = setup_coordinator_payer_channel(payer_to_payee_invoice_amount, &coordinator, &payer).await; diff --git a/crates/ln-dlc-node/src/tests/mod.rs b/crates/ln-dlc-node/src/tests/mod.rs index ff30584a4..65088f7a9 100644 --- a/crates/ln-dlc-node/src/tests/mod.rs +++ b/crates/ln-dlc-node/src/tests/mod.rs @@ -34,9 +34,9 @@ use dlc_manager::subchannel::SubChannel; use dlc_manager::subchannel::SubChannelState; use dlc_manager::Storage; use futures::Future; +use lightning::events::Event; use lightning::ln::channelmanager::ChannelDetails; use lightning::util::config::UserConfig; -use lightning::util::events::Event; use rand::distributions::Alphanumeric; use rand::thread_rng; use rand::Rng; @@ -189,7 +189,7 @@ impl Node { let node = Arc::new(node); let event_handler = event_handler_factory(node.clone(), ldk_event_sender); - let running = node.start(event_handler)?; + let running = node.start(event_handler, false)?; tracing::debug!(%name, info = %node.info, "Node started"); diff --git a/crates/ln-dlc-node/src/tests/multi_hop_payment.rs b/crates/ln-dlc-node/src/tests/multi_hop_payment.rs index 6c073eaf5..200b40eaf 100644 --- a/crates/ln-dlc-node/src/tests/multi_hop_payment.rs +++ b/crates/ln-dlc-node/src/tests/multi_hop_payment.rs @@ -40,9 +40,7 @@ async fn multi_hop_payment() { .await .unwrap(); - // after creating the just-in-time channel. The coordinator should have exactly 2 usable - // channels with short channel ids. - wait_for_n_usable_channels(1, &payer).await.unwrap(); + wait_for_n_usable_channels(2, &coordinator).await.unwrap(); let payer_balance_before = payer.get_ldk_balance(); let coordinator_balance_before = coordinator.get_ldk_balance(); @@ -54,7 +52,7 @@ async fn multi_hop_payment() { // Act - let invoice_amount_sat = 1_000; + let invoice_amount_sat = 4_000; let invoice = payee .create_invoice(invoice_amount_sat, "".to_string(), 180) .unwrap(); diff --git a/crates/orderbook-commons/Cargo.toml b/crates/orderbook-commons/Cargo.toml index 13539d91b..c11cb90ef 100644 --- a/crates/orderbook-commons/Cargo.toml +++ b/crates/orderbook-commons/Cargo.toml @@ -7,8 +7,8 @@ edition = "2021" [dependencies] anyhow = "1" -bitcoin = "0.29" -lightning = "0.0.114" +bitcoin = "0.29.2" +lightning = "0.0.116" rust_decimal = { version = "1", features = ["serde-with-float"] } secp256k1 = { version = "0.24.3", features = ["serde"] } serde = { version = "1", features = ["derive"] } diff --git a/crates/tests-e2e/Cargo.toml b/crates/tests-e2e/Cargo.toml index 44e5a84c8..0ca1b7fe2 100644 --- a/crates/tests-e2e/Cargo.toml +++ b/crates/tests-e2e/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" [dependencies] anyhow = "1" -bitcoin = "0.29" +bitcoin = "0.29.2" coordinator = { path = "../../coordinator" } coordinator-commons = { path = "../coordinator-commons" } flutter_rust_bridge = "1.78.0" @@ -31,6 +31,6 @@ tracing-subscriber = { version = "0.3", features = ["env-filter"] } [dev-dependencies] assertables = "7.0.1" -bitcoin = "0.29" +bitcoin = "0.29.2" clap = { version = "4", features = ["derive"] } local-ip-address = "0.5.1" diff --git a/crates/tests-e2e/src/coordinator.rs b/crates/tests-e2e/src/coordinator.rs index 953b8d58d..ff5584855 100644 --- a/crates/tests-e2e/src/coordinator.rs +++ b/crates/tests-e2e/src/coordinator.rs @@ -89,7 +89,10 @@ impl Coordinator { Ok(()) } - pub async fn create_invoice(&self, amount: Option) -> Result { + pub async fn create_invoice( + &self, + amount: Option, + ) -> Result { let invoice_params = InvoiceParams { amount, description: Some("Fee for tests".to_string()), diff --git a/crates/tests-e2e/src/maker.rs b/crates/tests-e2e/src/maker.rs index fbc902247..f24485237 100644 --- a/crates/tests-e2e/src/maker.rs +++ b/crates/tests-e2e/src/maker.rs @@ -1,7 +1,7 @@ use anyhow::Context; use anyhow::Result; use bitcoin::Address; -use ln_dlc_node::lightning_invoice::Invoice; +use ln_dlc_node::lightning_invoice::Bolt11Invoice; use ln_dlc_node::node::NodeInfo; use maker::routes::Balance; use maker::routes::ChannelParams; @@ -43,7 +43,7 @@ impl Maker { Ok(()) } - pub async fn pay_invoice(&self, invoice: Invoice) -> Result<()> { + pub async fn pay_invoice(&self, invoice: Bolt11Invoice) -> Result<()> { let no_json: Option<()> = None; self.post(&format!("/api/pay-invoice/{invoice}"), no_json) .await?; diff --git a/crates/trade/Cargo.toml b/crates/trade/Cargo.toml index 25c842c92..644287d8e 100644 --- a/crates/trade/Cargo.toml +++ b/crates/trade/Cargo.toml @@ -8,7 +8,7 @@ description = "A common library for the shared model and functions for trading" [dependencies] anyhow = "1" -bdk = { version = "0.27.0", default-features = false, features = ["key-value-db", "use-esplora-blocking"] } +bdk = { version = "0.28.0", default-features = false, features = ["key-value-db", "use-esplora-blocking"] } reqwest = { version = "0.11", default-features = false, features = ["json"] } rust_decimal = { version = "1", features = ["serde-with-float"] } serde = { version = "1.0.152", features = ["serde_derive"] } diff --git a/maker/Cargo.toml b/maker/Cargo.toml index e73698c69..d4a86c16c 100644 --- a/maker/Cargo.toml +++ b/maker/Cargo.toml @@ -9,9 +9,9 @@ async-stream = "0.3" async-trait = "0.1" atty = "0.2.14" autometrics = { version = "0.5", features = ["prometheus-exporter"] } -axum = { version = "0.6.20", features = ["ws"] } -bdk = { version = "0.27.0", default-features = false, features = ["key-value-db", "use-esplora-blocking"] } -bitcoin = "0.29" +axum = { version = "0.6.7", features = ["ws"] } +bdk = { version = "0.28.0", default-features = false, features = ["key-value-db", "use-esplora-blocking"] } +bitcoin = "0.29.2" bitmex-client = { path = "../crates/bitmex-client" } bitmex-stream = { path = "../crates/bitmex-stream" } clap = { version = "4", features = ["derive"] } @@ -20,7 +20,7 @@ diesel_migrations = "2.0.0" futures = "0.3" hex = "0.4" lazy_static = "1.4.0" -lightning = { version = "0.0.114", features = ["max_level_trace"] } +lightning = { version = "0.0.116", features = ["max_level_trace"] } ln-dlc-node = { path = "../crates/ln-dlc-node" } # adding this as explicit dependency as we need the "vendored" flag for cross compilation openssl = { version = "0.10.55", features = ["vendored"] } diff --git a/maker/src/bin/maker.rs b/maker/src/bin/maker.rs index 8362acaa9..47c2fa91d 100644 --- a/maker/src/bin/maker.rs +++ b/maker/src/bin/maker.rs @@ -96,7 +96,7 @@ async fn main() -> Result<()> { )?); let event_handler = EventHandler::new(node.clone()); - let _running_node = node.start(event_handler)?; + let _running_node = node.start(event_handler, false)?; std::thread::spawn(node.sync_on_chain_wallet_periodically()); diff --git a/maker/src/ln/event_handler.rs b/maker/src/ln/event_handler.rs index 6404fb2fd..57097139d 100644 --- a/maker/src/ln/event_handler.rs +++ b/maker/src/ln/event_handler.rs @@ -7,7 +7,7 @@ use bitcoin::secp256k1::PublicKey; use ln_dlc_node::channel::Channel; use ln_dlc_node::channel::UserChannelId; use ln_dlc_node::lightning; -use ln_dlc_node::lightning::util::events::Event; +use ln_dlc_node::lightning::events::Event; use ln_dlc_node::ln::common_handlers; use ln_dlc_node::node::rust_dlc_manager::subchannel::LNChannelManager; use ln_dlc_node::node::ChannelManager; @@ -121,6 +121,7 @@ where next_channel_id, fee_earned_msat, claim_from_onchain_tx, + outbound_amount_forwarded_msat, } => { common_handlers::handle_payment_forwarded( &self.node, @@ -128,6 +129,7 @@ where next_channel_id, claim_from_onchain_tx, fee_earned_msat, + outbound_amount_forwarded_msat, ); } Event::PendingHTLCsForwardable { time_forwardable } => { @@ -174,10 +176,13 @@ where Event::PaymentClaimable { receiver_node_id: _, payment_hash, + onion_fields: _, amount_msat, + counterparty_skimmed_fee_msat: _, purpose, via_channel_id: _, via_user_channel_id: _, + claim_deadline: _, } => { common_handlers::handle_payment_claimable( &self.node.channel_manager, @@ -192,6 +197,27 @@ where "The maker should not support interceptable invoices!" ); } + Event::ChannelPending { + channel_id, + user_channel_id: _, + former_temporary_channel_id, + counterparty_node_id, + funding_txo, + } => { + let former_temporary_channel_id = + former_temporary_channel_id.unwrap_or([0; 32]).to_hex(); + tracing::debug!( + channel_id = channel_id.to_hex(), + former_temporary_channel_id, + counterparty_node_id = counterparty_node_id.to_string(), + funding_txo_tx_id = funding_txo.txid.to_string(), + funding_txo_tx_vout = funding_txo.vout, + "Channel pending" + ) + } + Event::BumpTransaction(_) => { + tracing::error!("We do not support anchor outputs yet"); + } }; Ok(()) @@ -241,7 +267,7 @@ where pub fn handle_channel_closed( &self, user_channel_id: u128, - reason: lightning::util::events::ClosureReason, + reason: lightning::events::ClosureReason, channel_id: [u8; 32], ) -> Result<(), anyhow::Error> { block_in_place(|| { @@ -254,13 +280,13 @@ where ); if let Some(channel) = self.node.storage.get_channel(&user_channel_id)? { - let channel = Channel::close_channel(channel, reason); + let channel = Channel::close_channel(channel, reason.clone()); self.node.storage.upsert_channel(channel)?; } self.node .sub_channel_manager - .notify_ln_channel_closed(channel_id)?; + .notify_ln_channel_closed(channel_id, &reason)?; anyhow::Ok(()) })?; diff --git a/mobile/native/Cargo.toml b/mobile/native/Cargo.toml index f6ac67bd3..00f101a56 100644 --- a/mobile/native/Cargo.toml +++ b/mobile/native/Cargo.toml @@ -9,9 +9,9 @@ crate-type = ["rlib", "cdylib", "staticlib"] [dependencies] anyhow = "1" base64 = "0.21.0" -bdk = { version = "0.27.0", default-features = false, features = ["key-value-db", "use-esplora-blocking"] } +bdk = { version = "0.28.0", default-features = false, features = ["key-value-db", "use-esplora-blocking"] } bip21 = "0.2.0" -bitcoin = "0.29" +bitcoin = "0.29.2" coordinator-commons = { path = "../../crates/coordinator-commons" } diesel = { version = "2.0.0", features = ["sqlite", "r2d2", "extras"] } diesel_migrations = "2.0.0" @@ -22,8 +22,8 @@ futures = "0.3" hex = "0.4" itertools = "0.10" libsqlite3-sys = { version = "0.25.2", features = ["bundled"] } -lightning = { version = "0.0.114" } -lightning-invoice = { version = "0.22" } +lightning = { version = "0.0.116" } +lightning-invoice = { version = "0.24" } ln-dlc-node = { path = "../../crates/ln-dlc-node" } openssl = { version = "0.10.55", features = ["vendored"] } orderbook-client = { path = "../../crates/orderbook-client" } diff --git a/mobile/native/src/db/mod.rs b/mobile/native/src/db/mod.rs index a4c3dddf6..eda12b774 100644 --- a/mobile/native/src/db/mod.rs +++ b/mobile/native/src/db/mod.rs @@ -343,7 +343,7 @@ pub fn get_payments() -> Result Result<()> { tracing::debug!(?descriptor, "Inserting spendable output"); @@ -355,7 +355,7 @@ pub fn insert_spendable_output( pub fn get_spendable_output( outpoint: lightning::chain::transaction::OutPoint, -) -> Result> { +) -> Result> { tracing::debug!(?outpoint, "Getting spendable output"); let mut db = connection()?; @@ -374,8 +374,7 @@ pub fn delete_spendable_output(outpoint: lightning::chain::transaction::OutPoint Ok(()) } -pub fn get_spendable_outputs( -) -> Result> { +pub fn get_spendable_outputs() -> Result> { let mut db = connection()?; let outputs = SpendableOutputQueryable::get_all(&mut db)?; diff --git a/mobile/native/src/db/models.rs b/mobile/native/src/db/models.rs index 3d1f3c609..da7e3aca6 100644 --- a/mobile/native/src/db/models.rs +++ b/mobile/native/src/db/models.rs @@ -994,13 +994,13 @@ fn outpoint_to_string(outpoint: lightning::chain::transaction::OutPoint) -> Stri impl From<( lightning::chain::transaction::OutPoint, - lightning::chain::keysinterface::SpendableOutputDescriptor, + lightning::sign::SpendableOutputDescriptor, )> for SpendableOutputInsertable { fn from( (outpoint, descriptor): ( lightning::chain::transaction::OutPoint, - lightning::chain::keysinterface::SpendableOutputDescriptor, + lightning::sign::SpendableOutputDescriptor, ), ) -> Self { let outpoint = outpoint_to_string(outpoint); @@ -1013,9 +1013,7 @@ impl } } -impl TryFrom - for lightning::chain::keysinterface::SpendableOutputDescriptor -{ +impl TryFrom for lightning::sign::SpendableOutputDescriptor { type Error = anyhow::Error; fn try_from(value: SpendableOutputQueryable) -> Result { @@ -1581,7 +1579,7 @@ pub mod test { .unwrap(), index: 2, }; - let descriptor = lightning::chain::keysinterface::SpendableOutputDescriptor::StaticOutput { + let descriptor = lightning::sign::SpendableOutputDescriptor::StaticOutput { outpoint, output: bitcoin::TxOut { value: 10_000, @@ -1604,7 +1602,7 @@ pub mod test { }; ( outpoint, - lightning::chain::keysinterface::SpendableOutputDescriptor::StaticOutput { + lightning::sign::SpendableOutputDescriptor::StaticOutput { outpoint, output: bitcoin::TxOut { value: 25_000, diff --git a/mobile/native/src/destination.rs b/mobile/native/src/destination.rs index 7b54ec008..a4a7b138b 100644 --- a/mobile/native/src/destination.rs +++ b/mobile/native/src/destination.rs @@ -4,8 +4,8 @@ use anyhow::Context; use anyhow::Result; use bitcoin::Address; use bitcoin::Amount; -use lightning_invoice::Invoice; -use lightning_invoice::InvoiceDescription; +use lightning_invoice::Bolt11Invoice; +use lightning_invoice::Bolt11InvoiceDescription; use std::ops::Add; use std::str::FromStr; use std::time::Duration; @@ -45,10 +45,11 @@ fn decode_address(request: String) -> Result { } fn decode_invoice(request: &str) -> Result { - let invoice = &Invoice::from_str(request).context("request is not valid BOLT11 invoice")?; + let invoice = + &Bolt11Invoice::from_str(request).context("request is not valid BOLT11 invoice")?; let description = match invoice.description() { - InvoiceDescription::Direct(direct) => direct.to_string(), - InvoiceDescription::Hash(_) => "".to_string(), + Bolt11InvoiceDescription::Direct(direct) => direct.to_string(), + Bolt11InvoiceDescription::Hash(_) => "".to_string(), }; let timestamp = invoice.timestamp(); diff --git a/mobile/native/src/ln_dlc/lightning_subscriber.rs b/mobile/native/src/ln_dlc/lightning_subscriber.rs index b559e6115..45eaffb68 100644 --- a/mobile/native/src/ln_dlc/lightning_subscriber.rs +++ b/mobile/native/src/ln_dlc/lightning_subscriber.rs @@ -1,7 +1,7 @@ use crate::event; use crate::event::EventInternal; use crate::ln_dlc::node::Node; -use lightning::util::events::Event; +use lightning::events::Event; use tokio::sync::watch::Receiver; impl Node { diff --git a/mobile/native/src/ln_dlc/mod.rs b/mobile/native/src/ln_dlc/mod.rs index 36b9aa8ee..3c5ec1f87 100644 --- a/mobile/native/src/ln_dlc/mod.rs +++ b/mobile/native/src/ln_dlc/mod.rs @@ -1,4 +1,3 @@ -use self::node::WalletHistories; use crate::api; use crate::api::PaymentFlow; use crate::api::SendPayment; @@ -15,6 +14,7 @@ use crate::event::EventInternal; use crate::ln_dlc::channel_status::track_channel_status; use crate::ln_dlc::node::Node; use crate::ln_dlc::node::NodeStorage; +use crate::ln_dlc::node::WalletHistories; use crate::trade::order; use crate::trade::order::FailureReason; use crate::trade::order::Order; @@ -43,7 +43,6 @@ use bitcoin::PackedLockTime; use bitcoin::Transaction; use bitcoin::TxIn; use bitcoin::TxOut; -pub use channel_status::ChannelStatus; use coordinator_commons::CollaborativeRevertData; use coordinator_commons::LiquidityOption; use coordinator_commons::LspConfig; @@ -51,15 +50,17 @@ use coordinator_commons::OnboardingParam; use coordinator_commons::TradeParams; use itertools::chain; use itertools::Itertools; +use lightning::events::Event; use lightning::ln::channelmanager::ChannelDetails; -use lightning::util::events::Event; -use lightning_invoice::Invoice; use ln_dlc_node::channel::Channel; use ln_dlc_node::channel::UserChannelId; use ln_dlc_node::channel::JIT_FEE_INVOICE_DESCRIPTION_PREFIX; use ln_dlc_node::config::app_config; +use ln_dlc_node::lightning_invoice::Bolt11Invoice; use ln_dlc_node::node::rust_dlc_manager; use ln_dlc_node::node::rust_dlc_manager::subchannel::LNChannelManager; +use ln_dlc_node::node::rust_dlc_manager::subchannel::LnDlcChannelSigner; +use ln_dlc_node::node::rust_dlc_manager::subchannel::LnDlcSignerProvider; use ln_dlc_node::node::rust_dlc_manager::subchannel::SubChannel; use ln_dlc_node::node::rust_dlc_manager::subchannel::SubChannelState; use ln_dlc_node::node::rust_dlc_manager::ChannelId; @@ -97,10 +98,12 @@ use trade::ContractSymbol; mod lightning_subscriber; mod node; +mod recover_rollover; mod sync_position_to_dlc; pub mod channel_status; -mod recover_rollover; + +pub use channel_status::ChannelStatus; const PROCESS_INCOMING_DLC_MESSAGES_INTERVAL: Duration = Duration::from_millis(200); const UPDATE_WALLET_HISTORY_INTERVAL: Duration = Duration::from_secs(5); @@ -271,7 +274,7 @@ pub fn run(data_dir: String, seed_dir: String, runtime: &Runtime) -> Result<()> let node = Arc::new(node); let event_handler = AppEventHandler::new(node.clone(), Some(event_sender)); - let _running = node.start(event_handler)?; + let _running = node.start(event_handler, true)?; let node = Arc::new(Node::new(node, _running)); // Refresh the wallet balance and history eagerly so that it can complete before the @@ -457,7 +460,7 @@ fn keep_wallet_balance_and_history_up_to_date(node: &Node) -> Result<()> { None => return None, }; - let decoded_invoice = match details.invoice.as_deref().map(Invoice::from_str) { + let decoded_invoice = match details.invoice.as_deref().map(Bolt11Invoice::from_str) { Some(Ok(inv)) => { tracing::trace!(?inv, "Decoded invoice"); Some(inv) @@ -713,35 +716,38 @@ pub fn collaborative_revert_channel( trader_amount: Amount, execution_price: Decimal, ) -> Result<()> { - let node = NODE.try_get().context("failed to get ln dlc node")?; - + let node = NODE.try_get().context("Failed to get Node")?; let node = node.inner.clone(); - let sub_channels = node.list_dlc_channels()?; - let subchannel = sub_channels + let channel_id_hex = hex::encode(channel_id); + + let subchannels = node.list_dlc_channels()?; + let subchannel = subchannels .iter() .find(|c| c.channel_id == channel_id) - .context("Could not find provided channel")?; + .with_context(|| format!("Could not find subchannel {channel_id_hex}"))?; - let channel_manager = node.channel_manager.clone(); - let details = channel_manager + let details = node + .channel_manager .get_channel_details(&subchannel.channel_id) - .context("Could not get channel details")?; + .with_context(|| { + format!("Could not get channel details for subchannel {channel_id_hex}") + })?; - let out_point = details + let funding_outpoint = details .original_funding_outpoint .or(details.funding_txo) - .context("Could not find original funding outpoint")?; + .context("Could not find original funding TXO for subchannel {channel_id_hex}")?; - let address = node.get_unused_address(); + let trader_address = node.get_unused_address(); let collab_revert_tx = Transaction { version: 2, lock_time: PackedLockTime::ZERO, input: vec![TxIn { previous_output: OutPoint { - txid: out_point.txid, - vout: out_point.index as u32, + txid: funding_outpoint.txid, + vout: funding_outpoint.index as u32, }, script_sig: Default::default(), sequence: Default::default(), @@ -754,83 +760,72 @@ pub fn collaborative_revert_channel( }, TxOut { value: trader_amount.to_sat(), - script_pubkey: address.script_pubkey(), + script_pubkey: trader_address.script_pubkey(), }, ], }; - let mut own_sig = None; - - channel_manager - .with_channel_lock_no_check( - &subchannel.channel_id, - &subchannel.counter_party, - |channel_lock| { - channel_manager.sign_with_fund_key_cb(channel_lock, &mut |fund_sk| { - let secp = Secp256k1::new(); - - own_sig = Some( - dlc::util::get_raw_sig_for_tx_input( - &secp, - &collab_revert_tx, - 0, - &subchannel.original_funding_redeemscript, - subchannel.fund_value_satoshis, - fund_sk, - ) - .expect("to be able to get raw sig for tx input"), - ); - }); - Ok(()) - }, - ) - .expect("To be able to get channel lock"); - if let Some(own_sig) = own_sig { - let data = CollaborativeRevertData { - channel_id: hex::encode(subchannel.channel_id), - transaction: collab_revert_tx, - signature: own_sig, - }; + let own_sig = { + let signer = node + .keys_manager + .derive_ln_dlc_channel_signer(subchannel.fund_value_satoshis, details.channel_keys_id); + + signer + .get_holder_split_tx_signature( + &Secp256k1::new(), + &collab_revert_tx, + &subchannel.original_funding_redeemscript, + subchannel.fund_value_satoshis, + ) + .context("Could not get own signature for collaborative revert transaction")? + }; - let client = reqwest_client(); - let runtime = get_or_create_tokio_runtime()?; - runtime.spawn({ - let sub_channel = subchannel.clone(); - async move { - match client - .post(format!( - "http://{}/api/channels/revertconfirm", - config::get_http_endpoint(), - )) - .json(&data) - .send() - .await - { - Ok(response) => match response.text().await { - Ok(response) => { - tracing::info!( - response, - "Received response from confirming reverting a channel" + let data = CollaborativeRevertData { + channel_id: channel_id_hex, + transaction: collab_revert_tx, + signature: own_sig, + }; + + let client = reqwest_client(); + let runtime = get_or_create_tokio_runtime()?; + runtime.spawn({ + let subchannel = subchannel.clone(); + async move { + match client + .post(format!( + "http://{}/api/channels/revertconfirm", + config::get_http_endpoint(), + )) + .json(&data) + .send() + .await + { + Ok(response) => match response.text().await { + Ok(response) => { + tracing::info!( + response, + "Received response from confirming reverting a channel" + ); + if let Err(e) = + update_state_after_collab_revert(subchannel, execution_price) + { + tracing::error!( + "Failed to update state after collaborative revert confirmation: {e:#}" ); - if let Err(e) = - update_state_after_collab_revert(sub_channel, execution_price) - { - tracing::error!( - "Failed to update state after collab revert. {e:#}" - ); - } } - Err(error) => { - tracing::error!("Failed at confirming reverting a channel {error:#}"); - } - }, - Err(err) => { - tracing::error!("Could not confirm collaborative revert {err:#}"); } + Err(e) => { + tracing::error!( + "Failed to decode collaborative revert confirmation response text: {e:#}" + ); + } + }, + Err(e) => { + tracing::error!("Failed to confirm collaborative revert: {e:#}"); } } - }); - } + } + }); Ok(()) } @@ -989,7 +984,10 @@ pub fn liquidity_options() -> Result> { Ok(lsp_config.liquidity_options) } -pub fn create_onboarding_invoice(amount_sats: u64, liquidity_option_id: i32) -> Result { +pub fn create_onboarding_invoice( + amount_sats: u64, + liquidity_option_id: i32, +) -> Result { let runtime = get_or_create_tokio_runtime()?; runtime.block_on(async { @@ -1057,7 +1055,7 @@ pub fn create_onboarding_invoice(amount_sats: u64, liquidity_option_id: i32) -> }) } -pub fn create_invoice(amount_sats: Option) -> Result { +pub fn create_invoice(amount_sats: Option) -> Result { let node = NODE.get(); let final_route_hint_hop = node @@ -1075,7 +1073,7 @@ pub fn create_invoice(amount_sats: Option) -> Result { pub fn send_payment(payment: SendPayment) -> Result<()> { match payment { SendPayment::Lightning { invoice, amount } => { - let invoice = Invoice::from_str(&invoice)?; + let invoice = Bolt11Invoice::from_str(&invoice)?; NODE.get().inner.pay_invoice(&invoice, amount)?; } SendPayment::OnChain { address, amount } => { diff --git a/mobile/native/src/ln_dlc/node.rs b/mobile/native/src/ln_dlc/node.rs index 82538b37a..769037a29 100644 --- a/mobile/native/src/ln_dlc/node.rs +++ b/mobile/native/src/ln_dlc/node.rs @@ -15,13 +15,13 @@ use dlc_messages::sub_channel::SubChannelRevoke; use dlc_messages::ChannelMessage; use dlc_messages::Message; use dlc_messages::SubChannelMessage; -use lightning::chain::keysinterface::DelayedPaymentOutputDescriptor; -use lightning::chain::keysinterface::SpendableOutputDescriptor; -use lightning::chain::keysinterface::StaticPaymentOutputDescriptor; use lightning::chain::transaction::OutPoint; use lightning::ln::PaymentHash; use lightning::ln::PaymentPreimage; use lightning::ln::PaymentSecret; +use lightning::sign::DelayedPaymentOutputDescriptor; +use lightning::sign::SpendableOutputDescriptor; +use lightning::sign::StaticPaymentOutputDescriptor; use ln_dlc_node::channel::Channel; use ln_dlc_node::node; use ln_dlc_node::node::dlc_message_name; diff --git a/mobile/native/src/ln_dlc/sync_position_to_dlc.rs b/mobile/native/src/ln_dlc/sync_position_to_dlc.rs index 6cae71cb3..1f384cb88 100644 --- a/mobile/native/src/ln_dlc/sync_position_to_dlc.rs +++ b/mobile/native/src/ln_dlc/sync_position_to_dlc.rs @@ -412,6 +412,7 @@ mod test { own_fund_pk: get_dummy_pubkey(), counter_fund_pk: get_dummy_pubkey(), counter_party_secrets: CounterpartyCommitmentSecrets::new(), + channel_keys_id: Some([0; 32]), } }