diff --git a/Cargo.toml b/Cargo.toml index 26de80123..8d57cd393 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,21 +28,21 @@ panic = 'abort' # Abort on panic default = [] [dependencies] -lightning = { version = "0.0.118", features = ["std"] } -lightning-invoice = { version = "0.26.0" } -lightning-net-tokio = { version = "0.0.118" } -lightning-persister = { version = "0.0.118" } -lightning-background-processor = { version = "0.0.118", features = ["futures"] } -lightning-rapid-gossip-sync = { version = "0.0.118" } -lightning-transaction-sync = { version = "0.0.118", features = ["esplora-async-https"] } - -# lightning = { git = "https://github.com/lightningdevkit/rust-lightning", branch="main", features = ["std"] } -# lightning-invoice = { git = "https://github.com/lightningdevkit/rust-lightning", branch="main" } -# lightning-net-tokio = { git = "https://github.com/lightningdevkit/rust-lightning", branch="main" } -# lightning-persister = { git = "https://github.com/lightningdevkit/rust-lightning", branch="main" } -# lightning-background-processor = { git = "https://github.com/lightningdevkit/rust-lightning", branch="main", features = ["futures"] } -# lightning-rapid-gossip-sync = { git = "https://github.com/lightningdevkit/rust-lightning", branch="main" } -# lightning-transaction-sync = { git = "https://github.com/lightningdevkit/rust-lightning", branch="main", features = ["esplora-async"] } +lightning = { version = "0.0.119", features = ["std"] } +lightning-invoice = { version = "0.27.0" } +lightning-net-tokio = { version = "0.0.119" } +lightning-persister = { version = "0.0.119" } +lightning-background-processor = { version = "0.0.119", features = ["futures"] } +lightning-rapid-gossip-sync = { version = "0.0.119" } +lightning-transaction-sync = { version = "0.0.119", features = ["esplora-async-https"] } + +#lightning = { git = "https://github.com/lightningdevkit/rust-lightning", branch="main", features = ["std"] } +#lightning-invoice = { git = "https://github.com/lightningdevkit/rust-lightning", branch="main" } +#lightning-net-tokio = { git = "https://github.com/lightningdevkit/rust-lightning", branch="main" } +#lightning-persister = { git = "https://github.com/lightningdevkit/rust-lightning", branch="main" } +#lightning-background-processor = { git = "https://github.com/lightningdevkit/rust-lightning", branch="main", features = ["futures"] } +#lightning-rapid-gossip-sync = { git = "https://github.com/lightningdevkit/rust-lightning", branch="main" } +#lightning-transaction-sync = { git = "https://github.com/lightningdevkit/rust-lightning", branch="main", features = ["esplora-async"] } #lightning = { path = "../rust-lightning/lightning", features = ["std"] } #lightning-invoice = { path = "../rust-lightning/lightning-invoice" } @@ -52,18 +52,18 @@ lightning-transaction-sync = { version = "0.0.118", features = ["esplora-async-h #lightning-rapid-gossip-sync = { path = "../rust-lightning/lightning-rapid-gossip-sync" } #lightning-transaction-sync = { path = "../rust-lightning/lightning-transaction-sync", features = ["esplora-async"] } -bdk = { version = "0.28.0", default-features = false, features = ["std", "async-interface", "use-esplora-async", "sqlite-bundled", "keys-bip39"]} +bdk = { version = "0.29.0", default-features = false, features = ["std", "async-interface", "use-esplora-async", "sqlite-bundled", "keys-bip39"]} reqwest = { version = "0.11", default-features = false, features = ["json", "rustls-tls"] } rusqlite = { version = "0.28.0", features = ["bundled"] } -bitcoin = "0.29.2" +bitcoin = "0.30.2" bip39 = "2.0.0" rand = "0.8.5" chrono = { version = "0.4", default-features = false, features = ["clock"] } futures = "0.3" tokio = { version = "1", default-features = false, features = [ "rt-multi-thread", "time", "sync" ] } -esplora-client = { version = "0.4", default-features = false } +esplora-client = { version = "0.6", default-features = false } libc = "0.2" uniffi = { version = "0.25.1", features = ["build"], optional = true } @@ -74,16 +74,16 @@ vss-client = "0.1" winapi = { version = "0.3", features = ["winbase"] } [dev-dependencies] -lightning = { version = "0.0.118", features = ["std", "_test_utils"] } +lightning = { version = "0.0.119", features = ["std", "_test_utils"] } #lightning = { git = "https://github.com/lightningdevkit/rust-lightning", branch="main", features = ["std", "_test_utils"] } -electrsd = { version = "0.22.0", features = ["legacy", "esplora_a33e97e1", "bitcoind_23_0"] } -electrum-client = "0.12.0" +electrsd = { version = "0.26.0", features = ["legacy", "esplora_a33e97e1", "bitcoind_25_0"] } +electrum-client = { version = "0.18.0", default-features = true } proptest = "1.0.0" regex = "1.5.6" [target.'cfg(cln_test)'.dev-dependencies] clightningrpc = { version = "0.3.0-beta.8", default-features = false } -bitcoincore-rpc = { version = "0.16.0", default-features = false } +bitcoincore-rpc = { version = "0.17.0", default-features = false } [build-dependencies] uniffi = { version = "0.25.1", features = ["build"], optional = true } diff --git a/README.md b/README.md index 1877168a0..4e7ba42ab 100644 --- a/README.md +++ b/README.md @@ -11,11 +11,10 @@ LDK Node is a self-custodial Lightning node in library form. Its central goal is The primary abstraction of the library is the [`Node`][api_docs_node], which can be retrieved by setting up and configuring a [`Builder`][api_docs_builder] to your liking and calling one of the `build` methods. `Node` can then be controlled via commands such as `start`, `stop`, `connect_open_channel`, `send_payment`, etc. ```rust -use ldk_node::Builder; +use ldk_node::{Builder, Network}; use ldk_node::lightning_invoice::Invoice; use ldk_node::lightning::ln::msgs::SocketAddress; use ldk_node::bitcoin::secp256k1::PublicKey; -use ldk_node::bitcoin::Network; use std::str::FromStr; fn main() { diff --git a/src/builder.rs b/src/builder.rs index a44a05f0c..b60123844 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -9,8 +9,8 @@ use crate::peer_store::PeerStore; use crate::sweep::OutputSweeper; use crate::tx_broadcaster::TransactionBroadcaster; use crate::types::{ - ChainMonitor, ChannelManager, FakeMessageRouter, GossipSync, KeysManager, NetworkGraph, - OnionMessenger, PeerManager, + ChainMonitor, ChannelManager, FakeMessageRouter, GossipSync, KeysManager, Network, + NetworkGraph, OnionMessenger, PeerManager, }; use crate::wallet::Wallet; use crate::LogLevel; @@ -47,8 +47,6 @@ use bdk::blockchain::esplora::EsploraBlockchain; use bdk::database::SqliteDatabase; use bdk::template::Bip84; -use bitcoin::Network; - use bip39::Mnemonic; use bitcoin::BlockHash; @@ -436,7 +434,7 @@ fn build_with_store_internal( logger: Arc, kv_store: Arc, ) -> Result, BuildError> { // Initialize the on-chain wallet and chain access - let xprv = bitcoin::util::bip32::ExtendedPrivKey::new_master(config.network, &seed_bytes) + let xprv = bitcoin::bip32::ExtendedPrivKey::new_master(config.network.into(), &seed_bytes) .map_err(|e| { log_error!(logger, "Failed to derive master secret: {}", e); BuildError::InvalidSeedBytes @@ -445,7 +443,7 @@ fn build_with_store_internal( let wallet_name = bdk::wallet::wallet_name_from_descriptor( Bip84(xprv, bdk::KeychainKind::External), Some(Bip84(xprv, bdk::KeychainKind::Internal)), - config.network, + config.network.into(), &Secp256k1::new(), ) .map_err(|e| { @@ -459,7 +457,7 @@ fn build_with_store_internal( let bdk_wallet = bdk::Wallet::new( Bip84(xprv, bdk::KeychainKind::External), Some(Bip84(xprv, bdk::KeychainKind::Internal)), - config.network, + config.network.into(), database, ) .map_err(|e| { @@ -537,7 +535,7 @@ fn build_with_store_internal( Ok(graph) => Arc::new(graph), Err(e) => { if e.kind() == std::io::ErrorKind::NotFound { - Arc::new(NetworkGraph::new(config.network, Arc::clone(&logger))) + Arc::new(NetworkGraph::new(config.network.into(), Arc::clone(&logger))) } else { return Err(BuildError::ReadFailed); } @@ -629,10 +627,10 @@ fn build_with_store_internal( } else { // We're starting a fresh node. let genesis_block_hash = - bitcoin::blockdata::constants::genesis_block(config.network).block_hash(); + bitcoin::blockdata::constants::genesis_block(config.network.into()).block_hash(); let chain_params = ChainParameters { - network: config.network, + network: config.network.into(), best_block: BestBlock::new(genesis_block_hash, 0), }; channelmanager::ChannelManager::new( diff --git a/src/event.rs b/src/event.rs index 3d7e4599b..56c8f74a2 100644 --- a/src/event.rs +++ b/src/event.rs @@ -23,8 +23,9 @@ use lightning::util::errors::APIError; use lightning::util::persist::KVStore; use lightning::util::ser::{Readable, ReadableArgs, Writeable, Writer}; +use bitcoin::blockdata::locktime::absolute::LockTime; use bitcoin::secp256k1::PublicKey; -use bitcoin::{LockTime, OutPoint}; +use bitcoin::OutPoint; use rand::{thread_rng, Rng}; use std::collections::VecDeque; use std::ops::Deref; @@ -786,6 +787,7 @@ where LdkEvent::HTLCIntercepted { .. } => {} LdkEvent::BumpTransaction(_) => {} LdkEvent::InvoiceRequestFailed { .. } => {} + LdkEvent::ConnectionNeeded { .. } => {} } } } diff --git a/src/fee_estimator.rs b/src/fee_estimator.rs index 4116c992d..c12242784 100644 --- a/src/fee_estimator.rs +++ b/src/fee_estimator.rs @@ -8,6 +8,8 @@ use lightning::chain::chaininterface::{ use bdk::FeeRate; use esplora_client::AsyncClient as EsploraClient; +use bitcoin::blockdata::weight::Weight; + use std::collections::HashMap; use std::ops::Deref; use std::sync::RwLock; @@ -33,7 +35,6 @@ where pub(crate) async fn update_fee_estimates(&self) -> Result<(), Error> { let confirmation_targets = vec![ ConfirmationTarget::OnChainSweep, - ConfirmationTarget::MaxAllowedNonAnchorChannelRemoteFee, ConfirmationTarget::MinAllowedAnchorChannelRemoteFee, ConfirmationTarget::MinAllowedNonAnchorChannelRemoteFee, ConfirmationTarget::AnchorChannelFee, @@ -43,7 +44,6 @@ where for target in confirmation_targets { let num_blocks = match target { ConfirmationTarget::OnChainSweep => 6, - ConfirmationTarget::MaxAllowedNonAnchorChannelRemoteFee => 1, ConfirmationTarget::MinAllowedAnchorChannelRemoteFee => 1008, ConfirmationTarget::MinAllowedNonAnchorChannelRemoteFee => 144, ConfirmationTarget::AnchorChannelFee => 1008, @@ -77,12 +77,9 @@ where // LDK 0.0.118 introduced changes to the `ConfirmationTarget` semantics that // require some post-estimation adjustments to the fee rates, which we do here. let adjusted_fee_rate = match target { - ConfirmationTarget::MaxAllowedNonAnchorChannelRemoteFee => { - let really_high_prio = fee_rate.as_sat_per_vb() * 10.0; - FeeRate::from_sat_per_vb(really_high_prio) - } ConfirmationTarget::MinAllowedNonAnchorChannelRemoteFee => { - let slightly_less_than_background = fee_rate.fee_wu(1000) - 250; + let slightly_less_than_background = + fee_rate.fee_wu(Weight::from_wu(1000)) - 250; FeeRate::from_sat_per_kwu(slightly_less_than_background as f32) } _ => fee_rate, @@ -94,7 +91,7 @@ where self.logger, "Fee rate estimation updated for {:?}: {} sats/kwu", target, - adjusted_fee_rate.fee_wu(1000) + adjusted_fee_rate.fee_wu(Weight::from_wu(1000)) ); } Ok(()) @@ -105,7 +102,6 @@ where let fallback_sats_kwu = match confirmation_target { ConfirmationTarget::OnChainSweep => 5000, - ConfirmationTarget::MaxAllowedNonAnchorChannelRemoteFee => 25 * 250, ConfirmationTarget::MinAllowedAnchorChannelRemoteFee => FEERATE_FLOOR_SATS_PER_KW, ConfirmationTarget::MinAllowedNonAnchorChannelRemoteFee => FEERATE_FLOOR_SATS_PER_KW, ConfirmationTarget::AnchorChannelFee => 500, @@ -125,7 +121,7 @@ where L::Target: Logger, { fn get_est_sat_per_1000_weight(&self, confirmation_target: ConfirmationTarget) -> u32 { - (self.estimate_fee_rate(confirmation_target).fee_wu(1000) as u32) + (self.estimate_fee_rate(confirmation_target).fee_wu(Weight::from_wu(1000)) as u32) .max(FEERATE_FLOOR_SATS_PER_KW) } } diff --git a/src/lib.rs b/src/lib.rs index 0ffd7d062..2f09f2fd3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -26,11 +26,10 @@ //! [`send_payment`], etc.: //! //! ```no_run -//! use ldk_node::Builder; +//! use ldk_node::{Builder, Network}; //! use ldk_node::lightning_invoice::Bolt11Invoice; //! use ldk_node::lightning::ln::msgs::SocketAddress; //! use ldk_node::bitcoin::secp256k1::PublicKey; -//! use ldk_node::bitcoin::Network; //! use std::str::FromStr; //! //! fn main() { @@ -125,7 +124,7 @@ use types::{ Broadcaster, ChainMonitor, ChannelManager, FeeEstimator, KeysManager, NetworkGraph, PeerManager, Router, Scorer, Sweeper, Wallet, }; -pub use types::{ChannelDetails, PeerDetails, UserChannelId}; +pub use types::{ChannelDetails, Network, PeerDetails, UserChannelId}; use logger::{log_error, log_info, log_trace, FilesystemLogger, Logger}; @@ -150,7 +149,6 @@ use lightning_invoice::{payment, Bolt11Invoice, Currency}; use bitcoin::hashes::sha256::Hash as Sha256; use bitcoin::hashes::Hash; use bitcoin::secp256k1::PublicKey; -use bitcoin::Network; use bitcoin::{Address, Txid}; @@ -745,6 +743,7 @@ impl Node { Some(background_scorer), sleeper, true, + || Some(SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap()), ) .await .unwrap_or_else(|e| { @@ -1004,6 +1003,7 @@ impl Node { channel_amount_sats, push_msat, user_channel_id, + None, Some(user_config), ) { Ok(_) => { @@ -1116,7 +1116,10 @@ impl Node { return Err(Error::NotRunning); } - let payment_hash = PaymentHash((*invoice.payment_hash()).into_inner()); + let (payment_hash, recipient_onion, route_params) = payment::payment_parameters_from_invoice(&invoice).map_err(|_| { + log_error!(self.logger, "Failed to send payment due to the given invoice being \"zero-amount\". Please use send_payment_using_amount instead."); + Error::InvalidInvoice + })?; if let Some(payment) = self.payment_store.get(&payment_hash) { if payment.status == PaymentStatus::Pending @@ -1128,13 +1131,17 @@ impl Node { } let payment_secret = Some(*invoice.payment_secret()); + let payment_id = PaymentId(invoice.payment_hash().to_byte_array()); + let retry_strategy = Retry::Timeout(LDK_PAYMENT_RETRY_TIMEOUT); - match lightning_invoice::payment::pay_invoice( - &invoice, - Retry::Timeout(LDK_PAYMENT_RETRY_TIMEOUT), - self.channel_manager.as_ref(), + match self.channel_manager.send_payment( + payment_hash, + recipient_onion, + payment_id, + route_params, + retry_strategy, ) { - Ok(_payment_id) => { + Ok(()) => { let payee_pubkey = invoice.recover_payee_pub_key(); let amt_msat = invoice.amount_milli_satoshis().unwrap(); log_info!(self.logger, "Initiated sending {}msat to {}", amt_msat, payee_pubkey); @@ -1151,11 +1158,7 @@ impl Node { Ok(payment_hash) } - Err(payment::PaymentError::Invoice(e)) => { - log_error!(self.logger, "Failed to send payment due to invalid invoice: {}", e); - Err(Error::InvalidInvoice) - } - Err(payment::PaymentError::Sending(e)) => { + Err(e) => { log_error!(self.logger, "Failed to send payment: {:?}", e); match e { channelmanager::RetryableSendFailure::DuplicatePayment => { @@ -1202,7 +1205,7 @@ impl Node { } } - let payment_hash = PaymentHash((*invoice.payment_hash()).into_inner()); + let payment_hash = PaymentHash(invoice.payment_hash().to_byte_array()); if let Some(payment) = self.payment_store.get(&payment_hash) { if payment.status == PaymentStatus::Pending || payment.status == PaymentStatus::Succeeded @@ -1212,7 +1215,7 @@ impl Node { } } - let payment_id = PaymentId(invoice.payment_hash().into_inner()); + let payment_id = PaymentId(invoice.payment_hash().to_byte_array()); let payment_secret = invoice.payment_secret(); let expiry_time = invoice.duration_since_epoch().saturating_add(invoice.expiry_time()); let mut payment_params = PaymentParameters::from_node_id( @@ -1233,11 +1236,13 @@ impl Node { let retry_strategy = Retry::Timeout(LDK_PAYMENT_RETRY_TIMEOUT); let recipient_fields = RecipientOnionFields::secret_only(*payment_secret); - match self - .channel_manager - .send_payment(payment_hash, recipient_fields, payment_id, route_params, retry_strategy) - .map_err(payment::PaymentError::Sending) - { + match self.channel_manager.send_payment( + payment_hash, + recipient_fields, + payment_id, + route_params, + retry_strategy, + ) { Ok(_payment_id) => { let payee_pubkey = invoice.recover_payee_pub_key(); log_info!( @@ -1259,11 +1264,7 @@ impl Node { Ok(payment_hash) } - Err(payment::PaymentError::Invoice(e)) => { - log_error!(self.logger, "Failed to send payment due to invalid invoice: {}", e); - Err(Error::InvalidInvoice) - } - Err(payment::PaymentError::Sending(e)) => { + Err(e) => { log_error!(self.logger, "Failed to send payment: {:?}", e); match e { @@ -1298,7 +1299,7 @@ impl Node { } let payment_preimage = PaymentPreimage(self.keys_manager.get_secure_random_bytes()); - let payment_hash = PaymentHash(Sha256::hash(&payment_preimage.0).into_inner()); + let payment_hash = PaymentHash(Sha256::hash(&payment_preimage.0).to_byte_array()); if let Some(payment) = self.payment_store.get(&payment_hash) { if payment.status == PaymentStatus::Pending @@ -1381,17 +1382,19 @@ impl Node { return Err(Error::NotRunning); } + let (_payment_hash, _recipient_onion, route_params) = payment::payment_parameters_from_invoice(&invoice).map_err(|_| { + log_error!(self.logger, "Failed to send probes due to the given invoice being \"zero-amount\". Please use send_payment_probes_using_amount instead."); + Error::InvalidInvoice + })?; + let liquidity_limit_multiplier = Some(self.config.probing_liquidity_limit_multiplier); - payment::preflight_probe_invoice( - invoice, - &*self.channel_manager, - liquidity_limit_multiplier, - ) - .map_err(|e| { - log_error!(self.logger, "Failed to send payment probes: {:?}", e); - Error::ProbeSendingFailed - })?; + self.channel_manager + .send_preflight_probes(route_params, liquidity_limit_multiplier) + .map_err(|e| { + log_error!(self.logger, "Failed to send payment probes: {:?}", e); + Error::ProbeSendingFailed + })?; Ok(()) } @@ -1441,27 +1444,35 @@ impl Node { return Err(Error::NotRunning); } - if let Some(invoice_amount_msat) = invoice.amount_milli_satoshis() { + let (_payment_hash, _recipient_onion, route_params) = if let Some(invoice_amount_msat) = + invoice.amount_milli_satoshis() + { if amount_msat < invoice_amount_msat { log_error!( self.logger, "Failed to send probes as the given amount needs to be at least the invoice amount: required {}msat, gave {}msat.", invoice_amount_msat, amount_msat); return Err(Error::InvalidAmount); } - } + + payment::payment_parameters_from_invoice(&invoice).map_err(|_| { + log_error!(self.logger, "Failed to send probes due to the given invoice unexpectedly being \"zero-amount\"."); + Error::InvalidInvoice + })? + } else { + payment::payment_parameters_from_zero_amount_invoice(&invoice, amount_msat).map_err(|_| { + log_error!(self.logger, "Failed to send probes due to the given invoice unexpectedly being not \"zero-amount\"."); + Error::InvalidInvoice + })? + }; let liquidity_limit_multiplier = Some(self.config.probing_liquidity_limit_multiplier); - payment::preflight_probe_zero_value_invoice( - invoice, - amount_msat, - &*self.channel_manager, - liquidity_limit_multiplier, - ) - .map_err(|e| { - log_error!(self.logger, "Failed to send payment probes: {:?}", e); - Error::ProbeSendingFailed - })?; + self.channel_manager + .send_preflight_probes(route_params, liquidity_limit_multiplier) + .map_err(|e| { + log_error!(self.logger, "Failed to send payment probes: {:?}", e); + Error::ProbeSendingFailed + })?; Ok(()) } @@ -1485,7 +1496,7 @@ impl Node { fn receive_payment_inner( &self, amount_msat: Option, description: &str, expiry_secs: u32, ) -> Result { - let currency = Currency::from(self.config.network); + let currency = Currency::from(bitcoin::Network::from(self.config.network)); let keys_manager = Arc::clone(&self.keys_manager); let invoice = match lightning_invoice::utils::create_invoice_from_channelmanager( &self.channel_manager, @@ -1507,7 +1518,7 @@ impl Node { } }; - let payment_hash = PaymentHash((*invoice.payment_hash()).into_inner()); + let payment_hash = PaymentHash(invoice.payment_hash().to_byte_array()); let payment = PaymentDetails { hash: payment_hash, preimage: None, @@ -1538,8 +1549,7 @@ impl Node { /// /// For example, you could retrieve all stored outbound payments as follows: /// ``` - /// # use ldk_node::{Builder, Config, PaymentDirection}; - /// # use ldk_node::bitcoin::Network; + /// # use ldk_node::{Builder, Config, Network, PaymentDirection}; /// # let mut config = Config::default(); /// # config.network = Network::Regtest; /// # config.storage_dir_path = "/tmp/ldk_node_test/".to_string(); diff --git a/src/logger.rs b/src/logger.rs index b0504a354..5f8627e07 100644 --- a/src/logger.rs +++ b/src/logger.rs @@ -50,7 +50,7 @@ impl FilesystemLogger { } } impl Logger for FilesystemLogger { - fn log(&self, record: &Record) { + fn log(&self, record: Record) { if record.level < self.level { return; } diff --git a/src/sweep.rs b/src/sweep.rs index eac8b4dcb..d23afb029 100644 --- a/src/sweep.rs +++ b/src/sweep.rs @@ -15,8 +15,10 @@ use lightning::sign::{EntropySource, SpendableOutputDescriptor}; use lightning::util::persist::KVStore; use lightning::util::ser::Writeable; +use bitcoin::blockdata::block::Header; +use bitcoin::blockdata::locktime::absolute::LockTime; use bitcoin::secp256k1::Secp256k1; -use bitcoin::{BlockHash, BlockHeader, LockTime, PackedLockTime, Transaction, Txid}; +use bitcoin::{BlockHash, Transaction, Txid}; use std::ops::Deref; use std::sync::{Arc, Mutex}; @@ -40,11 +42,13 @@ pub(crate) struct SpendableOutputInfo { impl SpendableOutputInfo { fn to_watched_output(&self) -> WatchedOutput { match &self.descriptor { - SpendableOutputDescriptor::StaticOutput { outpoint, output } => WatchedOutput { - block_hash: self.first_broadcast_hash, - outpoint: *outpoint, - script_pubkey: output.script_pubkey.clone(), - }, + SpendableOutputDescriptor::StaticOutput { outpoint, output, channel_keys_id: _ } => { + WatchedOutput { + block_hash: self.first_broadcast_hash, + outpoint: *outpoint, + script_pubkey: output.script_pubkey.clone(), + } + } SpendableOutputDescriptor::DelayedPaymentOutput(output) => WatchedOutput { block_hash: self.first_broadcast_hash, outpoint: output.outpoint, @@ -304,8 +308,7 @@ where log_error!(self.logger, "Failed to get destination address from wallet: {}", e); })?; - let locktime: PackedLockTime = - LockTime::from_height(cur_height).map_or(PackedLockTime::ZERO, |l| l.into()); + let locktime = LockTime::from_height(cur_height).unwrap_or(LockTime::ZERO); let output_descriptors = output_descriptors.iter().collect::>(); self.keys_manager.spend_spendable_outputs( @@ -351,7 +354,7 @@ where L::Target: Logger, { fn filtered_block_connected( - &self, header: &BlockHeader, txdata: &chain::transaction::TransactionData, height: u32, + &self, header: &Header, txdata: &chain::transaction::TransactionData, height: u32, ) { { let best_block = self.best_block.lock().unwrap(); @@ -365,7 +368,7 @@ where self.best_block_updated(header, height); } - fn block_disconnected(&self, header: &BlockHeader, height: u32) { + fn block_disconnected(&self, header: &Header, height: u32) { let new_height = height - 1; { let mut best_block = self.best_block.lock().unwrap(); @@ -399,7 +402,7 @@ where L::Target: Logger, { fn transactions_confirmed( - &self, header: &BlockHeader, txdata: &chain::transaction::TransactionData, height: u32, + &self, header: &Header, txdata: &chain::transaction::TransactionData, height: u32, ) { let mut locked_outputs = self.outputs.lock().unwrap(); for (_, tx) in txdata { @@ -438,20 +441,26 @@ where ); } - fn best_block_updated(&self, header: &BlockHeader, height: u32) { + fn best_block_updated(&self, header: &Header, height: u32) { *self.best_block.lock().unwrap() = BestBlock::new(header.block_hash(), height); self.prune_confirmed_outputs(); self.rebroadcast_if_necessary(); } - fn get_relevant_txids(&self) -> Vec<(Txid, Option)> { + fn get_relevant_txids(&self) -> Vec<(Txid, u32, Option)> { let locked_outputs = self.outputs.lock().unwrap(); locked_outputs .iter() .filter_map(|o| { if let Some(confirmation_hash) = o.confirmation_hash { - if let Some(latest_spending_tx) = o.latest_spending_tx.as_ref() { - return Some((latest_spending_tx.txid(), Some(confirmation_hash))); + if let Some(confirmation_height) = o.confirmation_height { + if let Some(latest_spending_tx) = o.latest_spending_tx.as_ref() { + return Some(( + latest_spending_tx.txid(), + confirmation_height, + Some(confirmation_hash), + )); + } } } diff --git a/src/test/functional_tests.rs b/src/test/functional_tests.rs index dbb120b93..3600ee116 100644 --- a/src/test/functional_tests.rs +++ b/src/test/functional_tests.rs @@ -2,7 +2,7 @@ use crate::builder::NodeBuilder; use crate::io::test_utils::TestSyncStore; use crate::test::utils::*; use crate::test::utils::{expect_channel_pending_event, expect_event, open_channel, random_config}; -use crate::{Error, Event, Node, PaymentDirection, PaymentStatus}; +use crate::{Error, Event, Network, Node, PaymentDirection, PaymentStatus}; use bitcoin::Amount; use electrsd::bitcoind::BitcoinD; @@ -409,7 +409,7 @@ fn multi_hop_sending() { #[test] fn connect_to_public_testnet_esplora() { let mut config = random_config(); - config.network = bitcoin::Network::Testnet; + config.network = Network::Testnet; let mut builder = NodeBuilder::from_config(config); builder.set_esplora_server("https://blockstream.info/testnet/api".to_string()); let node = builder.build().unwrap(); diff --git a/src/test/utils.rs b/src/test/utils.rs index 6b415f9d2..ea1ac1bc0 100644 --- a/src/test/utils.rs +++ b/src/test/utils.rs @@ -1,16 +1,16 @@ use crate::builder::NodeBuilder; use crate::io::test_utils::TestSyncStore; -use crate::{Config, Event, Node}; +use crate::{Config, Event, Network, Node}; use lightning::ln::msgs::SocketAddress; use lightning::util::logger::{Level, Logger, Record}; use lightning::util::persist::KVStore; -use bitcoin::{Address, Amount, Network, OutPoint, Txid}; +use bitcoin::{Address, Amount, OutPoint, Txid}; use bitcoind::bitcoincore_rpc::RpcApi; use electrsd::bitcoind::bitcoincore_rpc::bitcoincore_rpc_json::AddressType; +use electrsd::electrum_client::ElectrumApi; use electrsd::{bitcoind, bitcoind::BitcoinD, ElectrsD}; -use electrum_client::ElectrumApi; use regex; @@ -119,7 +119,7 @@ impl TestLogger { } impl Logger for TestLogger { - fn log(&self, record: &Record) { + fn log(&self, record: Record) { *self .lines .lock() @@ -238,6 +238,8 @@ pub fn generate_blocks_and_wait(bitcoind: &BitcoinD, electrsd: &ElectrsD, num: u let address = bitcoind .client .get_new_address(Some("test"), Some(AddressType::Legacy)) + .expect("failed to get new address") + .require_network(bitcoin::Network::Regtest) .expect("failed to get new address"); // TODO: expect this Result once the WouldBlock issue is resolved upstream. let _block_hashes_res = bitcoind.client.generate_to_address(num as u64, &address); diff --git a/src/types.rs b/src/types.rs index 4edba65c3..b5926cb99 100644 --- a/src/types.rs +++ b/src/types.rs @@ -1,6 +1,7 @@ use crate::logger::FilesystemLogger; use crate::sweep::OutputSweeper; +use lightning::blinded_path::BlindedPath; use lightning::chain::chainmonitor; use lightning::ln::channelmanager::ChannelDetails as LdkChannelDetails; use lightning::ln::msgs::RoutingMessageHandler; @@ -10,16 +11,18 @@ use lightning::ln::ChannelId; use lightning::routing::gossip; use lightning::routing::router::DefaultRouter; use lightning::routing::scoring::{ProbabilisticScorer, ProbabilisticScoringFeeParameters}; -use lightning::sign::InMemorySigner; +use lightning::sign::{EntropySource, InMemorySigner}; use lightning::util::config::ChannelConfig as LdkChannelConfig; use lightning::util::config::MaxDustHTLCExposure as LdkMaxDustHTLCExposure; use lightning::util::ser::{Readable, Writeable, Writer}; use lightning_net_tokio::SocketDescriptor; use lightning_transaction_sync::EsploraSyncClient; -use bitcoin::secp256k1::PublicKey; +use bitcoin::secp256k1::{self, PublicKey, Secp256k1}; use bitcoin::OutPoint; +use std::fmt; +use std::str::FromStr; use std::sync::{Arc, Mutex, RwLock}; pub(crate) type ChainMonitor = chainmonitor::ChainMonitor< @@ -117,6 +120,15 @@ impl lightning::onion_message::MessageRouter for FakeMessageRouter { ) -> Result { unimplemented!() } + fn create_blinded_paths< + ES: EntropySource + ?Sized, + T: secp256k1::Signing + secp256k1::Verification, + >( + &self, _recipient: PublicKey, _peers: Vec, _entropy_source: &ES, + _secp_ctx: &Secp256k1, + ) -> Result, ()> { + unreachable!() + } } pub(crate) type Sweeper = OutputSweeper< @@ -127,6 +139,58 @@ pub(crate) type Sweeper = OutputSweeper< Arc, >; +/// The cryptocurrency network to act on. +#[derive(Copy, PartialEq, Eq, PartialOrd, Ord, Clone, Hash, Debug)] +pub enum Network { + /// Mainnet Bitcoin. + Bitcoin, + /// Bitcoin's testnet network. + Testnet, + /// Bitcoin's signet network. + Signet, + /// Bitcoin's regtest network. + Regtest, +} + +impl TryFrom for Network { + type Error = (); + + fn try_from(network: bitcoin::Network) -> Result { + match network { + bitcoin::Network::Bitcoin => Ok(Self::Bitcoin), + bitcoin::Network::Testnet => Ok(Self::Testnet), + bitcoin::Network::Signet => Ok(Self::Signet), + bitcoin::Network::Regtest => Ok(Self::Regtest), + _ => Err(()), + } + } +} + +impl From for bitcoin::Network { + fn from(network: Network) -> Self { + match network { + Network::Bitcoin => bitcoin::Network::Bitcoin, + Network::Testnet => bitcoin::Network::Testnet, + Network::Signet => bitcoin::Network::Signet, + Network::Regtest => bitcoin::Network::Regtest, + } + } +} + +impl fmt::Display for Network { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + bitcoin::Network::from(*self).fmt(f) + } +} + +impl FromStr for Network { + type Err = (); + + fn from_str(s: &str) -> Result { + bitcoin::Network::from_str(s).map_err(|_| ())?.try_into() + } +} + /// A local, potentially user-provided, identifier of a channel. /// /// By default, this will be randomly generated for the user to ensure local uniqueness. diff --git a/src/uniffi_types.rs b/src/uniffi_types.rs index 74606d83d..964571c7f 100644 --- a/src/uniffi_types.rs +++ b/src/uniffi_types.rs @@ -42,7 +42,7 @@ impl UniffiCustomTypeConverter for Address { fn into_custom(val: Self::Builtin) -> uniffi::Result { if let Ok(addr) = Address::from_str(&val) { - return Ok(addr); + return Ok(addr.assume_checked()); } Err(Error::InvalidAddress.into()) @@ -76,7 +76,7 @@ impl UniffiCustomTypeConverter for PaymentHash { fn into_custom(val: Self::Builtin) -> uniffi::Result { if let Ok(hash) = Sha256::from_str(&val) { - Ok(PaymentHash(hash.into_inner())) + Ok(PaymentHash(hash.to_byte_array())) } else { Err(Error::InvalidPaymentHash.into()) } diff --git a/src/wallet.rs b/src/wallet.rs index 0f60adccc..d3e6c0a47 100644 --- a/src/wallet.rs +++ b/src/wallet.rs @@ -20,10 +20,11 @@ use bdk::FeeRate; use bdk::{SignOptions, SyncOptions}; use bitcoin::bech32::u5; +use bitcoin::blockdata::locktime::absolute::LockTime; use bitcoin::secp256k1::ecdh::SharedSecret; use bitcoin::secp256k1::ecdsa::{RecoverableSignature, Signature}; use bitcoin::secp256k1::{PublicKey, Scalar, Secp256k1, Signing}; -use bitcoin::{LockTime, PackedLockTime, Script, Transaction, TxOut, Txid}; +use bitcoin::{ScriptBuf, Transaction, TxOut, Txid}; use std::ops::Deref; use std::sync::{Arc, Condvar, Mutex}; @@ -114,7 +115,7 @@ where } pub(crate) fn create_funding_transaction( - &self, output_script: Script, value_sats: u64, confirmation_target: ConfirmationTarget, + &self, output_script: ScriptBuf, value_sats: u64, confirmation_target: ConfirmationTarget, locktime: LockTime, ) -> Result { let fee_rate = FeeRate::from_sat_per_kwu( @@ -280,8 +281,8 @@ where /// See [`KeysManager::spend_spendable_outputs`] for documentation on this method. pub fn spend_spendable_outputs( &self, descriptors: &[&SpendableOutputDescriptor], outputs: Vec, - change_destination_script: Script, feerate_sat_per_1000_weight: u32, - locktime: Option, secp_ctx: &Secp256k1, + change_destination_script: ScriptBuf, feerate_sat_per_1000_weight: u32, + locktime: Option, secp_ctx: &Secp256k1, ) -> Result { self.inner.spend_spendable_outputs( descriptors, @@ -366,7 +367,7 @@ where E::Target: FeeEstimator, L::Target: Logger, { - type Signer = InMemorySigner; + type EcdsaSigner = InMemorySigner; fn generate_channel_keys_id( &self, inbound: bool, channel_value_satoshis: u64, user_channel_id: u128, @@ -376,15 +377,15 @@ where fn derive_channel_signer( &self, channel_value_satoshis: u64, channel_keys_id: [u8; 32], - ) -> Self::Signer { + ) -> Self::EcdsaSigner { self.inner.derive_channel_signer(channel_value_satoshis, channel_keys_id) } - fn read_chan_signer(&self, reader: &[u8]) -> Result { + fn read_chan_signer(&self, reader: &[u8]) -> Result { self.inner.read_chan_signer(reader) } - fn get_destination_script(&self) -> Result { + fn get_destination_script(&self, _channel_keys_id: [u8; 32]) -> Result { let address = self.wallet.get_new_address().map_err(|e| { log_error!(self.logger, "Failed to retrieve new address from wallet: {}", e); })?; @@ -397,8 +398,8 @@ where })?; match address.payload { - bitcoin::util::address::Payload::WitnessProgram { version, program } => { - ShutdownScript::new_witness_program(version, &program).map_err(|e| { + bitcoin::address::Payload::WitnessProgram(program) => { + ShutdownScript::new_witness_program(&program).map_err(|e| { log_error!(self.logger, "Invalid shutdown script: {:?}", e); }) } diff --git a/tests/common.rs b/tests/common.rs index 6c92c4c1d..381827d7f 100644 --- a/tests/common.rs +++ b/tests/common.rs @@ -1,10 +1,10 @@ #![cfg(cln_test)] -use ldk_node::{Config, LogLevel}; +use ldk_node::{Config, LogLevel, Network}; use lightning::ln::msgs::SocketAddress; -use bitcoin::{Address, Amount, Network, OutPoint, Txid}; +use bitcoin::{Address, Amount, OutPoint, Txid}; use bitcoincore_rpc::bitcoincore_rpc_json::AddressType; use bitcoincore_rpc::Client as BitcoindClient; @@ -125,6 +125,8 @@ pub(crate) fn generate_blocks_and_wait( let cur_height = bitcoind.get_block_count().expect("failed to get current block height"); let address = bitcoind .get_new_address(Some("test"), Some(AddressType::Legacy)) + .expect("failed to get new address") + .require_network(bitcoin::Network::Regtest) .expect("failed to get new address"); // TODO: expect this Result once the WouldBlock issue is resolved upstream. let _block_hashes_res = bitcoind.generate_to_address(num as u64, &address);