From d9336f232c5aebb00706a759cf6381cbf9287d59 Mon Sep 17 00:00:00 2001 From: Jeffrey Czyz Date: Tue, 17 Feb 2026 21:49:00 -0600 Subject: [PATCH 1/5] Bump LDK dependency for major splicing API changes Update splice_in/splice_out to use new LDK two-phase funding API The LDK dependency bump introduced a new splicing API that separates negotiation from coin selection, letting LDK handle transaction construction internally rather than requiring manual UTXO selection and change address management. Generated with the assistance of AI (Claude Code). Co-Authored-By: Claude Opus 4.6 --- Cargo.toml | 26 +++---- src/lib.rs | 120 ++++++++++---------------------- src/wallet/mod.rs | 82 ++++++++++++++++++++-- tests/integration_tests_rust.rs | 2 +- 4 files changed, 128 insertions(+), 102 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index cb856fab1..2b3ecf32c 100755 --- a/Cargo.toml +++ b/Cargo.toml @@ -39,17 +39,17 @@ default = [] #lightning-liquidity = { version = "0.2.0", features = ["std"] } #lightning-macros = { version = "0.2.0" } -lightning = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "b6c17c593a5d7bacb18fe3b9f69074a0596ae8f0", features = ["std"] } -lightning-types = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "b6c17c593a5d7bacb18fe3b9f69074a0596ae8f0" } -lightning-invoice = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "b6c17c593a5d7bacb18fe3b9f69074a0596ae8f0", features = ["std"] } -lightning-net-tokio = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "b6c17c593a5d7bacb18fe3b9f69074a0596ae8f0" } -lightning-persister = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "b6c17c593a5d7bacb18fe3b9f69074a0596ae8f0", features = ["tokio"] } -lightning-background-processor = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "b6c17c593a5d7bacb18fe3b9f69074a0596ae8f0" } -lightning-rapid-gossip-sync = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "b6c17c593a5d7bacb18fe3b9f69074a0596ae8f0" } -lightning-block-sync = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "b6c17c593a5d7bacb18fe3b9f69074a0596ae8f0", features = ["rest-client", "rpc-client", "tokio"] } -lightning-transaction-sync = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "b6c17c593a5d7bacb18fe3b9f69074a0596ae8f0", features = ["esplora-async-https", "time", "electrum-rustls-ring"] } -lightning-liquidity = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "b6c17c593a5d7bacb18fe3b9f69074a0596ae8f0", features = ["std"] } -lightning-macros = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "b6c17c593a5d7bacb18fe3b9f69074a0596ae8f0" } +lightning = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "0a20fa5340b5788c2472febbfbb631506b15c42d", features = ["std"] } +lightning-types = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "0a20fa5340b5788c2472febbfbb631506b15c42d" } +lightning-invoice = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "0a20fa5340b5788c2472febbfbb631506b15c42d", features = ["std"] } +lightning-net-tokio = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "0a20fa5340b5788c2472febbfbb631506b15c42d" } +lightning-persister = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "0a20fa5340b5788c2472febbfbb631506b15c42d", features = ["tokio"] } +lightning-background-processor = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "0a20fa5340b5788c2472febbfbb631506b15c42d" } +lightning-rapid-gossip-sync = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "0a20fa5340b5788c2472febbfbb631506b15c42d" } +lightning-block-sync = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "0a20fa5340b5788c2472febbfbb631506b15c42d", features = ["rest-client", "rpc-client", "tokio"] } +lightning-transaction-sync = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "0a20fa5340b5788c2472febbfbb631506b15c42d", features = ["esplora-async-https", "time", "electrum-rustls-ring"] } +lightning-liquidity = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "0a20fa5340b5788c2472febbfbb631506b15c42d", features = ["std"] } +lightning-macros = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "0a20fa5340b5788c2472febbfbb631506b15c42d" } bdk_chain = { version = "0.23.0", default-features = false, features = ["std"] } bdk_esplora = { version = "0.22.0", default-features = false, features = ["async-https-rustls", "tokio"]} @@ -78,13 +78,13 @@ log = { version = "0.4.22", default-features = false, features = ["std"]} vss-client = { package = "vss-client-ng", version = "0.5" } prost = { version = "0.11.6", default-features = false} #bitcoin-payment-instructions = { version = "0.6" } -bitcoin-payment-instructions = { git = "https://github.com/tnull/bitcoin-payment-instructions", rev = "ea50a9d2a8da524b69a2af43233706666cf2ffa5" } +bitcoin-payment-instructions = { git = "https://github.com/jkczyz/bitcoin-payment-instructions", rev = "c8c470b0a4241a875bf1a1038482825e41c28bd2" } [target.'cfg(windows)'.dependencies] winapi = { version = "0.3", features = ["winbase"] } [dev-dependencies] -lightning = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "b6c17c593a5d7bacb18fe3b9f69074a0596ae8f0", features = ["std", "_test_utils"] } +lightning = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "0a20fa5340b5788c2472febbfbb631506b15c42d", features = ["std", "_test_utils"] } rand = { version = "0.9.2", default-features = false, features = ["std", "thread_rng", "os_rng"] } proptest = "1.0.0" regex = "1.5.6" diff --git a/src/lib.rs b/src/lib.rs index b0fc1b2ae..320f1662a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -138,12 +138,10 @@ use gossip::GossipSource; use graph::NetworkGraph; use io::utils::write_node_metrics; use lightning::chain::BestBlock; -use lightning::events::bump_transaction::{Input, Wallet as LdkWallet}; +use lightning::events::bump_transaction::Wallet as LdkWallet; use lightning::impl_writeable_tlv_based; -use lightning::ln::chan_utils::FUNDING_TRANSACTION_WITNESS_WEIGHT; use lightning::ln::channel_state::{ChannelDetails as LdkChannelDetails, ChannelShutdownState}; use lightning::ln::channelmanager::PaymentId; -use lightning::ln::funding::SpliceContribution; use lightning::ln::msgs::SocketAddress; use lightning::routing::gossip::NodeAlias; use lightning::sign::EntropySource; @@ -1295,84 +1293,37 @@ impl Node { { self.check_sufficient_funds_for_channel(splice_amount_sats, &counterparty_node_id)?; - const EMPTY_SCRIPT_SIG_WEIGHT: u64 = - 1 /* empty script_sig */ * bitcoin::constants::WITNESS_SCALE_FACTOR as u64; - - let funding_txo = channel_details.funding_txo.ok_or_else(|| { - log_error!(self.logger, "Failed to splice channel: channel not yet ready",); - Error::ChannelSplicingFailed - })?; - - let funding_output = channel_details.get_funding_output().ok_or_else(|| { - log_error!(self.logger, "Failed to splice channel: channel not yet ready"); - Error::ChannelSplicingFailed - })?; - - let shared_input = Input { - outpoint: funding_txo.into_bitcoin_outpoint(), - previous_utxo: funding_output.clone(), - satisfaction_weight: EMPTY_SCRIPT_SIG_WEIGHT + FUNDING_TRANSACTION_WITNESS_WEIGHT, - }; - - let shared_output = bitcoin::TxOut { - value: shared_input.previous_utxo.value + Amount::from_sat(splice_amount_sats), - // will not actually be the exact same script pubkey after splice - // but it is the same size and good enough for coin selection purposes - script_pubkey: funding_output.script_pubkey.clone(), - }; - let fee_rate = self.fee_estimator.estimate_fee_rate(ConfirmationTarget::ChannelFunding); - let inputs = self - .wallet - .select_confirmed_utxos(vec![shared_input], &[shared_output], fee_rate) - .map_err(|()| { - log_error!( - self.logger, - "Failed to splice channel: insufficient confirmed UTXOs", - ); + let funding_template = self + .channel_manager + .splice_channel(&channel_details.channel_id, &counterparty_node_id, fee_rate) + .map_err(|e| { + log_error!(self.logger, "Failed to splice channel: {:?}", e); Error::ChannelSplicingFailed })?; - let change_address = self.wallet.get_new_internal_address()?; - - let contribution = SpliceContribution::splice_in( - Amount::from_sat(splice_amount_sats), - inputs, - Some(change_address.script_pubkey()), - ); - - let funding_feerate_per_kw: u32 = match fee_rate.to_sat_per_kwu().try_into() { - Ok(fee_rate) => fee_rate, - Err(_) => { - debug_assert!(false); - fee_estimator::get_fallback_rate_for_target(ConfirmationTarget::ChannelFunding) - }, - }; + let contribution = self + .runtime + .block_on( + funding_template + .splice_in(Amount::from_sat(splice_amount_sats), Arc::clone(&self.wallet)), + ) + .map_err(|()| { + log_error!(self.logger, "Failed to splice channel: coin selection failed"); + Error::ChannelSplicingFailed + })?; self.channel_manager - .splice_channel( + .funding_contributed( &channel_details.channel_id, &counterparty_node_id, contribution, - funding_feerate_per_kw, None, ) .map_err(|e| { log_error!(self.logger, "Failed to splice channel: {:?}", e); - let tx = bitcoin::Transaction { - version: bitcoin::transaction::Version::TWO, - lock_time: bitcoin::absolute::LockTime::ZERO, - input: vec![], - output: vec![bitcoin::TxOut { - value: Amount::ZERO, - script_pubkey: change_address.script_pubkey(), - }], - }; - match self.wallet.cancel_tx(&tx) { - Ok(()) => Error::ChannelSplicingFailed, - Err(e) => e, - } + Error::ChannelSplicingFailed }) } else { log_error!( @@ -1381,7 +1332,6 @@ impl Node { user_channel_id, counterparty_node_id ); - Err(Error::ChannelSplicingFailed) } } @@ -1412,27 +1362,33 @@ impl Node { self.wallet.parse_and_validate_address(address)?; - let contribution = SpliceContribution::splice_out(vec![bitcoin::TxOut { + let fee_rate = self.fee_estimator.estimate_fee_rate(ConfirmationTarget::ChannelFunding); + + let funding_template = self + .channel_manager + .splice_channel(&channel_details.channel_id, &counterparty_node_id, fee_rate) + .map_err(|e| { + log_error!(self.logger, "Failed to splice channel: {:?}", e); + Error::ChannelSplicingFailed + })?; + + let outputs = vec![bitcoin::TxOut { value: Amount::from_sat(splice_amount_sats), script_pubkey: address.script_pubkey(), - }]); - - let fee_rate = self.fee_estimator.estimate_fee_rate(ConfirmationTarget::ChannelFunding); - let funding_feerate_per_kw: u32 = match fee_rate.to_sat_per_kwu().try_into() { - Ok(fee_rate) => fee_rate, - Err(_) => { - debug_assert!(false, "FeeRate should always fit within u32"); - log_error!(self.logger, "FeeRate should always fit within u32"); - fee_estimator::get_fallback_rate_for_target(ConfirmationTarget::ChannelFunding) - }, - }; + }]; + let contribution = self + .runtime + .block_on(funding_template.splice_out(outputs, Arc::clone(&self.wallet))) + .map_err(|()| { + log_error!(self.logger, "Failed to splice channel: coin selection failed"); + Error::ChannelSplicingFailed + })?; self.channel_manager - .splice_channel( + .funding_contributed( &channel_details.channel_id, &counterparty_node_id, contribution, - funding_feerate_per_kw, None, ) .map_err(|e| { diff --git a/src/wallet/mod.rs b/src/wallet/mod.rs index 520fff48b..c4e36b83c 100644 --- a/src/wallet/mod.rs +++ b/src/wallet/mod.rs @@ -26,6 +26,7 @@ use bitcoin::psbt::{self, Psbt}; use bitcoin::secp256k1::ecdh::SharedSecret; use bitcoin::secp256k1::ecdsa::{RecoverableSignature, Signature}; use bitcoin::secp256k1::{All, PublicKey, Scalar, Secp256k1, SecretKey}; +use bitcoin::transaction::Sequence; use bitcoin::{ Address, Amount, FeeRate, OutPoint, ScriptBuf, Transaction, TxOut, Txid, WPubkeyHash, Weight, WitnessProgram, WitnessVersion, @@ -34,8 +35,10 @@ use lightning::chain::chaininterface::{ BroadcasterInterface, INCREMENTAL_RELAY_FEE_SAT_PER_1000_WEIGHT, }; use lightning::chain::channelmonitor::ANTI_REORG_DELAY; -use lightning::chain::{BestBlock, Listen}; -use lightning::events::bump_transaction::{Input, Utxo, WalletSource}; +use lightning::chain::{BestBlock, ClaimId, Listen}; +use lightning::events::bump_transaction::{ + CoinSelection, CoinSelectionSource, Input, Utxo, WalletSource, +}; use lightning::ln::channelmanager::PaymentId; use lightning::ln::funding::FundingTxInput; use lightning::ln::inbound_payment::ExpandedKey; @@ -760,8 +763,10 @@ impl Wallet { pub(crate) fn select_confirmed_utxos( &self, must_spend: Vec, must_pay_to: &[TxOut], fee_rate: FeeRate, - ) -> Result, ()> { + ) -> Result { let mut locked_wallet = self.inner.lock().unwrap(); + let mut locked_persister = self.persister.lock().unwrap(); + debug_assert!(matches!( locked_wallet.public_descriptor(KeychainKind::External), ExtendedDescriptor::Wpkh(_) @@ -790,12 +795,14 @@ impl Wallet { tx_builder.fee_rate(fee_rate); tx_builder.exclude_unconfirmed(); - tx_builder + let unsigned_tx = tx_builder .finish() .map_err(|e| { log_error!(self.logger, "Failed to select confirmed UTXOs: {}", e); })? - .unsigned_tx + .unsigned_tx; + + let confirmed_utxos = unsigned_tx .input .iter() .filter(|txin| must_spend.iter().all(|input| input.outpoint != txin.previous_output)) @@ -805,7 +812,31 @@ impl Wallet { .map(|tx_details| tx_details.tx.deref().clone()) .map(|prevtx| FundingTxInput::new_p2wpkh(prevtx, txin.previous_output.vout)) }) - .collect::, ()>>() + .collect::, ()>>()?; + + if unsigned_tx.output.len() > must_pay_to.len() + 1 { + log_error!( + self.logger, + "Unexpected number of change outputs during coin selection: {}", + unsigned_tx.output.len() - must_pay_to.len(), + ); + return Err(()); + } + + let change_output = unsigned_tx + .output + .into_iter() + .filter(|txout| must_pay_to.iter().all(|output| output != txout)) + .next(); + + if change_output.is_some() { + locked_wallet.persist(&mut locked_persister).map_err(|e| { + log_error!(self.logger, "Failed to persist wallet: {}", e); + () + })?; + } + + Ok(CoinSelection { confirmed_utxos, change_output }) } fn list_confirmed_utxos_inner(&self) -> Result, ()> { @@ -881,6 +912,7 @@ impl Wallet { }, satisfaction_weight: 1 /* empty script_sig */ * WITNESS_SCALE_FACTOR as u64 + 1 /* witness items */ + 1 /* schnorr sig len */ + 64, // schnorr sig + sequence: Sequence::ENABLE_RBF_NO_LOCKTIME, }; utxos.push(utxo); }, @@ -1339,9 +1371,47 @@ impl WalletSource for Wallet { async move { self.get_change_script_inner() } } + fn get_prevtx<'a>( + &'a self, outpoint: OutPoint, + ) -> impl Future> + Send + 'a { + async move { + let locked_wallet = self.inner.lock().unwrap(); + locked_wallet + .tx_details(outpoint.txid) + .map(|tx_details| tx_details.tx.deref().clone()) + .ok_or_else(|| { + log_error!( + self.logger, + "Failed to get previous transaction for {}", + outpoint.txid + ); + }) + } + } + + fn sign_psbt<'a>( + &'a self, psbt: Psbt, + ) -> impl Future> + Send + 'a { + async move { self.sign_psbt_inner(psbt) } + } +} + +// Anchor bumping uses LdkWallet for coin selection, which wraps a WalletSource to implement +// CoinSelectionSource. Splicing uses this implementation of coin selection instead. +impl CoinSelectionSource for Wallet { + fn select_confirmed_utxos<'a>( + &'a self, claim_id: Option, must_spend: Vec, must_pay_to: &'a [TxOut], + target_feerate_sat_per_1000_weight: u32, _max_tx_weight: u64, + ) -> impl Future> + Send + 'a { + debug_assert!(claim_id.is_none()); + let fee_rate = FeeRate::from_sat_per_kwu(target_feerate_sat_per_1000_weight as u64); + async move { self.select_confirmed_utxos(must_spend, must_pay_to, fee_rate) } + } + fn sign_psbt<'a>( &'a self, psbt: Psbt, ) -> impl Future> + Send + 'a { + debug_assert!(false); async move { self.sign_psbt_inner(psbt) } } } diff --git a/tests/integration_tests_rust.rs b/tests/integration_tests_rust.rs index a16a395a6..61c9c8281 100644 --- a/tests/integration_tests_rust.rs +++ b/tests/integration_tests_rust.rs @@ -984,7 +984,7 @@ async fn splice_channel() { expect_channel_ready_event!(node_a, node_b.node_id()); expect_channel_ready_event!(node_b, node_a.node_id()); - let expected_splice_in_fee_sat = 252; + let expected_splice_in_fee_sat = 255; let payments = node_b.list_payments(); let payment = From a49b3d8ce7ff302f002aac007d45efb080554fc4 Mon Sep 17 00:00:00 2001 From: Jeffrey Czyz Date: Fri, 20 Feb 2026 10:18:15 -0600 Subject: [PATCH 2/5] Adapt to lightning-block-sync HTTP layer migration The latest LDK dependency bump replaced the custom HTTP implementation in lightning-block-sync with the `bitreq` crate, changing constructor signatures, error types, and response parsing constraints. Update the bitcoind chain source to use the new string-based URL constructors and structured error enums. Use a dedicated BitcoindClientError type since std::io::Error was only used previously because that was used upstream. Generated with the assistance of AI (Claude Code). Co-Authored-By: Claude Opus 4.6 --- Cargo.toml | 26 ++-- src/chain/bitcoind.rs | 321 ++++++++++++++++-------------------------- 2 files changed, 135 insertions(+), 212 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 2b3ecf32c..0afe8be0c 100755 --- a/Cargo.toml +++ b/Cargo.toml @@ -39,17 +39,17 @@ default = [] #lightning-liquidity = { version = "0.2.0", features = ["std"] } #lightning-macros = { version = "0.2.0" } -lightning = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "0a20fa5340b5788c2472febbfbb631506b15c42d", features = ["std"] } -lightning-types = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "0a20fa5340b5788c2472febbfbb631506b15c42d" } -lightning-invoice = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "0a20fa5340b5788c2472febbfbb631506b15c42d", features = ["std"] } -lightning-net-tokio = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "0a20fa5340b5788c2472febbfbb631506b15c42d" } -lightning-persister = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "0a20fa5340b5788c2472febbfbb631506b15c42d", features = ["tokio"] } -lightning-background-processor = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "0a20fa5340b5788c2472febbfbb631506b15c42d" } -lightning-rapid-gossip-sync = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "0a20fa5340b5788c2472febbfbb631506b15c42d" } -lightning-block-sync = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "0a20fa5340b5788c2472febbfbb631506b15c42d", features = ["rest-client", "rpc-client", "tokio"] } -lightning-transaction-sync = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "0a20fa5340b5788c2472febbfbb631506b15c42d", features = ["esplora-async-https", "time", "electrum-rustls-ring"] } -lightning-liquidity = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "0a20fa5340b5788c2472febbfbb631506b15c42d", features = ["std"] } -lightning-macros = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "0a20fa5340b5788c2472febbfbb631506b15c42d" } +lightning = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "16ea4c7e2e05573bcffb3d6aa92592c24d5132ae", features = ["std"] } +lightning-types = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "16ea4c7e2e05573bcffb3d6aa92592c24d5132ae" } +lightning-invoice = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "16ea4c7e2e05573bcffb3d6aa92592c24d5132ae", features = ["std"] } +lightning-net-tokio = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "16ea4c7e2e05573bcffb3d6aa92592c24d5132ae" } +lightning-persister = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "16ea4c7e2e05573bcffb3d6aa92592c24d5132ae", features = ["tokio"] } +lightning-background-processor = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "16ea4c7e2e05573bcffb3d6aa92592c24d5132ae" } +lightning-rapid-gossip-sync = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "16ea4c7e2e05573bcffb3d6aa92592c24d5132ae" } +lightning-block-sync = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "16ea4c7e2e05573bcffb3d6aa92592c24d5132ae", features = ["rest-client", "rpc-client", "tokio"] } +lightning-transaction-sync = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "16ea4c7e2e05573bcffb3d6aa92592c24d5132ae", features = ["esplora-async-https", "time", "electrum-rustls-ring"] } +lightning-liquidity = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "16ea4c7e2e05573bcffb3d6aa92592c24d5132ae", features = ["std"] } +lightning-macros = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "16ea4c7e2e05573bcffb3d6aa92592c24d5132ae" } bdk_chain = { version = "0.23.0", default-features = false, features = ["std"] } bdk_esplora = { version = "0.22.0", default-features = false, features = ["async-https-rustls", "tokio"]} @@ -78,13 +78,13 @@ log = { version = "0.4.22", default-features = false, features = ["std"]} vss-client = { package = "vss-client-ng", version = "0.5" } prost = { version = "0.11.6", default-features = false} #bitcoin-payment-instructions = { version = "0.6" } -bitcoin-payment-instructions = { git = "https://github.com/jkczyz/bitcoin-payment-instructions", rev = "c8c470b0a4241a875bf1a1038482825e41c28bd2" } +bitcoin-payment-instructions = { git = "https://github.com/jkczyz/bitcoin-payment-instructions", rev = "3daaed987e0948e9c82c233aec268c7b84d78eea" } [target.'cfg(windows)'.dependencies] winapi = { version = "0.3", features = ["winbase"] } [dev-dependencies] -lightning = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "0a20fa5340b5788c2472febbfbb631506b15c42d", features = ["std", "_test_utils"] } +lightning = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "16ea4c7e2e05573bcffb3d6aa92592c24d5132ae", features = ["std", "_test_utils"] } rand = { version = "0.9.2", default-features = false, features = ["std", "thread_rng", "os_rng"] } proptest = "1.0.0" regex = "1.5.6" diff --git a/src/chain/bitcoind.rs b/src/chain/bitcoind.rs index 8a7167022..26924d8af 100644 --- a/src/chain/bitcoind.rs +++ b/src/chain/bitcoind.rs @@ -6,6 +6,7 @@ // accordance with one or both of these licenses. use std::collections::{HashMap, VecDeque}; +use std::fmt; use std::future::Future; use std::sync::atomic::{AtomicU64, Ordering}; use std::sync::{Arc, Mutex, RwLock}; @@ -18,11 +19,11 @@ use lightning::chain::chaininterface::ConfirmationTarget as LdkConfirmationTarge use lightning::chain::{BestBlock, Listen}; use lightning::util::ser::Writeable; use lightning_block_sync::gossip::UtxoSource; -use lightning_block_sync::http::{HttpEndpoint, JsonResponse}; +use lightning_block_sync::http::{HttpClientError, JsonResponse}; use lightning_block_sync::init::{synchronize_listeners, validate_best_block_header}; use lightning_block_sync::poll::{ChainPoller, ChainTip, ValidatedBlockHeader}; use lightning_block_sync::rest::RestClient; -use lightning_block_sync::rpc::{RpcClient, RpcError}; +use lightning_block_sync::rpc::{RpcClient, RpcClientError}; use lightning_block_sync::{ BlockData, BlockHeaderData, BlockSource, BlockSourceError, BlockSourceErrorKind, Cache, SpvClient, @@ -705,10 +706,10 @@ pub enum BitcoindClient { impl BitcoindClient { /// Creates a new RPC API client for the chain interactions with Bitcoin Core. pub(crate) fn new_rpc(host: String, port: u16, rpc_user: String, rpc_password: String) -> Self { - let http_endpoint = endpoint(host, port); + let url = base_url(host, port); let rpc_credentials = rpc_credentials(rpc_user, rpc_password); - let rpc_client = Arc::new(RpcClient::new(&rpc_credentials, http_endpoint)); + let rpc_client = Arc::new(RpcClient::new(&rpc_credentials, url)); let latest_mempool_timestamp = AtomicU64::new(0); @@ -726,12 +727,12 @@ impl BitcoindClient { rest_host: String, rest_port: u16, rpc_host: String, rpc_port: u16, rpc_user: String, rpc_password: String, ) -> Self { - let rest_endpoint = endpoint(rest_host, rest_port).with_path("/rest".to_string()); - let rest_client = Arc::new(RestClient::new(rest_endpoint)); + let rest_url = format!("{}/rest", base_url(rest_host, rest_port)); + let rest_client = Arc::new(RestClient::new(rest_url)); - let rpc_endpoint = endpoint(rpc_host, rpc_port); + let rpc_url = base_url(rpc_host, rpc_port); let rpc_credentials = rpc_credentials(rpc_user, rpc_password); - let rpc_client = Arc::new(RpcClient::new(&rpc_credentials, rpc_endpoint)); + let rpc_client = Arc::new(RpcClient::new(&rpc_credentials, rpc_url)); let latest_mempool_timestamp = AtomicU64::new(0); @@ -755,22 +756,28 @@ impl BitcoindClient { } /// Broadcasts the provided transaction. - pub(crate) async fn broadcast_transaction(&self, tx: &Transaction) -> std::io::Result { + pub(crate) async fn broadcast_transaction( + &self, tx: &Transaction, + ) -> Result { match self { BitcoindClient::Rpc { rpc_client, .. } => { - Self::broadcast_transaction_inner(Arc::clone(rpc_client), tx).await + Self::broadcast_transaction_inner(Arc::clone(rpc_client), tx) + .await + .map_err(BitcoindClientError::Rpc) }, BitcoindClient::Rest { rpc_client, .. } => { // Bitcoin Core's REST interface does not support broadcasting transactions // so we use the RPC client. - Self::broadcast_transaction_inner(Arc::clone(rpc_client), tx).await + Self::broadcast_transaction_inner(Arc::clone(rpc_client), tx) + .await + .map_err(BitcoindClientError::Rpc) }, } } async fn broadcast_transaction_inner( rpc_client: Arc, tx: &Transaction, - ) -> std::io::Result { + ) -> Result { let tx_serialized = bitcoin::consensus::encode::serialize_hex(tx); let tx_json = serde_json::json!(tx_serialized); rpc_client.call_method::("sendrawtransaction", &[tx_json]).await @@ -780,16 +787,15 @@ impl BitcoindClient { /// confirmation within the provided `num_blocks`. pub(crate) async fn get_fee_estimate_for_target( &self, num_blocks: usize, estimation_mode: FeeRateEstimationMode, - ) -> std::io::Result { + ) -> Result { match self { - BitcoindClient::Rpc { rpc_client, .. } => { - Self::get_fee_estimate_for_target_inner( - Arc::clone(rpc_client), - num_blocks, - estimation_mode, - ) - .await - }, + BitcoindClient::Rpc { rpc_client, .. } => Self::get_fee_estimate_for_target_inner( + Arc::clone(rpc_client), + num_blocks, + estimation_mode, + ) + .await + .map_err(BitcoindClientError::Rpc), BitcoindClient::Rest { rpc_client, .. } => { // We rely on the internal RPC client to make this call, as this // operation is not supported by Bitcoin Core's REST interface. @@ -799,6 +805,7 @@ impl BitcoindClient { estimation_mode, ) .await + .map_err(BitcoindClientError::Rpc) }, } } @@ -806,7 +813,7 @@ impl BitcoindClient { /// Estimate the fee rate for the provided target number of blocks. async fn get_fee_estimate_for_target_inner( rpc_client: Arc, num_blocks: usize, estimation_mode: FeeRateEstimationMode, - ) -> std::io::Result { + ) -> Result { let num_blocks_json = serde_json::json!(num_blocks); let estimation_mode_json = serde_json::json!(estimation_mode); rpc_client @@ -819,13 +826,19 @@ impl BitcoindClient { } /// Gets the mempool minimum fee rate. - pub(crate) async fn get_mempool_minimum_fee_rate(&self) -> std::io::Result { + pub(crate) async fn get_mempool_minimum_fee_rate( + &self, + ) -> Result { match self { BitcoindClient::Rpc { rpc_client, .. } => { - Self::get_mempool_minimum_fee_rate_rpc(Arc::clone(rpc_client)).await + Self::get_mempool_minimum_fee_rate_rpc(Arc::clone(rpc_client)) + .await + .map_err(BitcoindClientError::Rpc) }, BitcoindClient::Rest { rest_client, .. } => { - Self::get_mempool_minimum_fee_rate_rest(Arc::clone(rest_client)).await + Self::get_mempool_minimum_fee_rate_rest(Arc::clone(rest_client)) + .await + .map_err(BitcoindClientError::Rest) }, } } @@ -833,7 +846,7 @@ impl BitcoindClient { /// Get the mempool minimum fee rate via RPC interface. async fn get_mempool_minimum_fee_rate_rpc( rpc_client: Arc, - ) -> std::io::Result { + ) -> Result { rpc_client .call_method::("getmempoolinfo", &[]) .await @@ -843,7 +856,7 @@ impl BitcoindClient { /// Get the mempool minimum fee rate via REST interface. async fn get_mempool_minimum_fee_rate_rest( rest_client: Arc, - ) -> std::io::Result { + ) -> Result { rest_client .request_resource::("mempool/info.json") .await @@ -853,13 +866,17 @@ impl BitcoindClient { /// Gets the raw transaction for the provided transaction ID. Returns `None` if not found. pub(crate) async fn get_raw_transaction( &self, txid: &Txid, - ) -> std::io::Result> { + ) -> Result, BitcoindClientError> { match self { BitcoindClient::Rpc { rpc_client, .. } => { - Self::get_raw_transaction_rpc(Arc::clone(rpc_client), txid).await + Self::get_raw_transaction_rpc(Arc::clone(rpc_client), txid) + .await + .map_err(BitcoindClientError::Rpc) }, BitcoindClient::Rest { rest_client, .. } => { - Self::get_raw_transaction_rest(Arc::clone(rest_client), txid).await + Self::get_raw_transaction_rest(Arc::clone(rest_client), txid) + .await + .map_err(BitcoindClientError::Rest) }, } } @@ -867,7 +884,7 @@ impl BitcoindClient { /// Retrieve raw transaction for provided transaction ID via the RPC interface. async fn get_raw_transaction_rpc( rpc_client: Arc, txid: &Txid, - ) -> std::io::Result> { + ) -> Result, RpcClientError> { let txid_hex = txid.to_string(); let txid_json = serde_json::json!(txid_hex); match rpc_client @@ -875,37 +892,16 @@ impl BitcoindClient { .await { Ok(resp) => Ok(Some(resp.0)), - Err(e) => match e.into_inner() { - Some(inner) => { - let rpc_error_res: Result, _> = inner.downcast(); - - match rpc_error_res { - Ok(rpc_error) => { - // Check if it's the 'not found' error code. - if rpc_error.code == -5 { - Ok(None) - } else { - Err(std::io::Error::new(std::io::ErrorKind::Other, rpc_error)) - } - }, - Err(_) => Err(std::io::Error::new( - std::io::ErrorKind::Other, - "Failed to process getrawtransaction response", - )), - } - }, - None => Err(std::io::Error::new( - std::io::ErrorKind::Other, - "Failed to process getrawtransaction response", - )), - }, + // Check if it's the 'not found' error code. + Err(RpcClientError::Rpc(rpc_error)) if rpc_error.code == -5 => Ok(None), + Err(e) => Err(e), } } /// Retrieve raw transaction for provided transaction ID via the REST interface. async fn get_raw_transaction_rest( rest_client: Arc, txid: &Txid, - ) -> std::io::Result> { + ) -> Result, HttpClientError> { let txid_hex = txid.to_string(); let tx_path = format!("tx/{}.json", txid_hex); match rest_client @@ -913,61 +909,30 @@ impl BitcoindClient { .await { Ok(resp) => Ok(Some(resp.0)), - Err(e) => match e.kind() { - std::io::ErrorKind::Other => { - match e.into_inner() { - Some(inner) => { - let http_error_res: Result, _> = inner.downcast(); - match http_error_res { - Ok(http_error) => { - // Check if it's the HTTP NOT_FOUND error code. - if &http_error.status_code == "404" { - Ok(None) - } else { - Err(std::io::Error::new( - std::io::ErrorKind::Other, - http_error, - )) - } - }, - Err(_) => { - let error_msg = - format!("Failed to process {} response.", tx_path); - Err(std::io::Error::new( - std::io::ErrorKind::Other, - error_msg.as_str(), - )) - }, - } - }, - None => { - let error_msg = format!("Failed to process {} response.", tx_path); - Err(std::io::Error::new(std::io::ErrorKind::Other, error_msg.as_str())) - }, - } - }, - _ => { - let error_msg = format!("Failed to process {} response.", tx_path); - Err(std::io::Error::new(std::io::ErrorKind::Other, error_msg.as_str())) - }, - }, + // Check if it's the HTTP NOT_FOUND error code. + Err(HttpClientError::Http(http_error)) if http_error.status_code == 404 => Ok(None), + Err(e) => Err(e), } } /// Retrieves the raw mempool. - pub(crate) async fn get_raw_mempool(&self) -> std::io::Result> { + pub(crate) async fn get_raw_mempool(&self) -> Result, BitcoindClientError> { match self { BitcoindClient::Rpc { rpc_client, .. } => { - Self::get_raw_mempool_rpc(Arc::clone(rpc_client)).await + Self::get_raw_mempool_rpc(Arc::clone(rpc_client)) + .await + .map_err(BitcoindClientError::Rpc) }, BitcoindClient::Rest { rest_client, .. } => { - Self::get_raw_mempool_rest(Arc::clone(rest_client)).await + Self::get_raw_mempool_rest(Arc::clone(rest_client)) + .await + .map_err(BitcoindClientError::Rest) }, } } /// Retrieves the raw mempool via the RPC interface. - async fn get_raw_mempool_rpc(rpc_client: Arc) -> std::io::Result> { + async fn get_raw_mempool_rpc(rpc_client: Arc) -> Result, RpcClientError> { let verbose_flag_json = serde_json::json!(false); rpc_client .call_method::("getrawmempool", &[verbose_flag_json]) @@ -976,7 +941,9 @@ impl BitcoindClient { } /// Retrieves the raw mempool via the REST interface. - async fn get_raw_mempool_rest(rest_client: Arc) -> std::io::Result> { + async fn get_raw_mempool_rest( + rest_client: Arc, + ) -> Result, HttpClientError> { rest_client .request_resource::( "mempool/contents.json?verbose=false", @@ -988,13 +955,17 @@ impl BitcoindClient { /// Retrieves an entry from the mempool if it exists, else return `None`. pub(crate) async fn get_mempool_entry( &self, txid: Txid, - ) -> std::io::Result> { + ) -> Result, BitcoindClientError> { match self { BitcoindClient::Rpc { rpc_client, .. } => { - Self::get_mempool_entry_inner(Arc::clone(rpc_client), txid).await + Self::get_mempool_entry_inner(Arc::clone(rpc_client), txid) + .await + .map_err(BitcoindClientError::Rpc) }, BitcoindClient::Rest { rpc_client, .. } => { - Self::get_mempool_entry_inner(Arc::clone(rpc_client), txid).await + Self::get_mempool_entry_inner(Arc::clone(rpc_client), txid) + .await + .map_err(BitcoindClientError::Rpc) }, } } @@ -1002,40 +973,19 @@ impl BitcoindClient { /// Retrieves the mempool entry of the provided transaction ID. async fn get_mempool_entry_inner( client: Arc, txid: Txid, - ) -> std::io::Result> { + ) -> Result, RpcClientError> { let txid_hex = txid.to_string(); let txid_json = serde_json::json!(txid_hex); match client.call_method::("getmempoolentry", &[txid_json]).await { Ok(resp) => Ok(Some(MempoolEntry { txid, time: resp.time, height: resp.height })), - Err(e) => match e.into_inner() { - Some(inner) => { - let rpc_error_res: Result, _> = inner.downcast(); - - match rpc_error_res { - Ok(rpc_error) => { - // Check if it's the 'not found' error code. - if rpc_error.code == -5 { - Ok(None) - } else { - Err(std::io::Error::new(std::io::ErrorKind::Other, rpc_error)) - } - }, - Err(_) => Err(std::io::Error::new( - std::io::ErrorKind::Other, - "Failed to process getmempoolentry response", - )), - } - }, - None => Err(std::io::Error::new( - std::io::ErrorKind::Other, - "Failed to process getmempoolentry response", - )), - }, + // Check if it's the 'not found' error code. + Err(RpcClientError::Rpc(rpc_error)) if rpc_error.code == -5 => Ok(None), + Err(e) => Err(e), } } - pub(crate) async fn update_mempool_entries_cache(&self) -> std::io::Result<()> { + pub(crate) async fn update_mempool_entries_cache(&self) -> Result<(), BitcoindClientError> { match self { BitcoindClient::Rpc { mempool_entries_cache, .. } => { self.update_mempool_entries_cache_inner(mempool_entries_cache).await @@ -1048,7 +998,7 @@ impl BitcoindClient { async fn update_mempool_entries_cache_inner( &self, mempool_entries_cache: &tokio::sync::Mutex>, - ) -> std::io::Result<()> { + ) -> Result<(), BitcoindClientError> { let mempool_txids = self.get_raw_mempool().await?; let mut mempool_entries_cache = mempool_entries_cache.lock().await; @@ -1079,7 +1029,7 @@ impl BitcoindClient { /// - transactions that have been evicted from the mempool, alongside the last time they were seen absent. pub(crate) async fn get_updated_mempool_transactions( &self, best_processed_height: u32, bdk_unconfirmed_txids: Vec, - ) -> std::io::Result<(Vec<(Transaction, u64)>, Vec<(Txid, u64)>)> { + ) -> Result<(Vec<(Transaction, u64)>, Vec<(Txid, u64)>), BitcoindClientError> { let mempool_txs = self.get_mempool_transactions_and_timestamp_at_height(best_processed_height).await?; let evicted_txids = @@ -1094,7 +1044,7 @@ impl BitcoindClient { /// emitted. pub(crate) async fn get_mempool_transactions_and_timestamp_at_height( &self, best_processed_height: u32, - ) -> std::io::Result> { + ) -> Result, BitcoindClientError> { match self { BitcoindClient::Rpc { latest_mempool_timestamp, @@ -1132,7 +1082,7 @@ impl BitcoindClient { mempool_entries_cache: &tokio::sync::Mutex>, mempool_txs_cache: &tokio::sync::Mutex>, best_processed_height: u32, - ) -> std::io::Result> { + ) -> Result, BitcoindClientError> { let prev_mempool_time = latest_mempool_timestamp.load(Ordering::Relaxed); let mut latest_time = prev_mempool_time; @@ -1194,7 +1144,7 @@ impl BitcoindClient { // wallet `Txid`s that don't appear in the mempool still. async fn get_evicted_mempool_txids_and_timestamp( &self, bdk_unconfirmed_txids: Vec, - ) -> std::io::Result> { + ) -> Result, BitcoindClientError> { match self { BitcoindClient::Rpc { latest_mempool_timestamp, mempool_entries_cache, .. } => { Self::get_evicted_mempool_txids_and_timestamp_inner( @@ -1219,7 +1169,7 @@ impl BitcoindClient { latest_mempool_timestamp: &AtomicU64, mempool_entries_cache: &tokio::sync::Mutex>, bdk_unconfirmed_txids: Vec, - ) -> std::io::Result> { + ) -> Result, BitcoindClientError> { let latest_mempool_timestamp = latest_mempool_timestamp.load(Ordering::Relaxed); let mempool_entries_cache = mempool_entries_cache.lock().await; let evicted_txids = bdk_unconfirmed_txids @@ -1275,17 +1225,13 @@ impl BlockSource for BitcoindClient { pub(crate) struct FeeResponse(pub FeeRate); impl TryInto for JsonResponse { - type Error = std::io::Error; - fn try_into(self) -> std::io::Result { + type Error = String; + fn try_into(self) -> Result { if !self.0["errors"].is_null() { - return Err(std::io::Error::new( - std::io::ErrorKind::Other, - self.0["errors"].to_string(), - )); + return Err(self.0["errors"].to_string()); } - let fee_rate_btc_per_kvbyte = self.0["feerate"] - .as_f64() - .ok_or(std::io::Error::new(std::io::ErrorKind::Other, "Failed to parse fee rate"))?; + let fee_rate_btc_per_kvbyte = + self.0["feerate"].as_f64().ok_or("Failed to parse fee rate".to_string())?; // Bitcoin Core gives us a feerate in BTC/KvB. // Thus, we multiply by 25_000_000 (10^8 / 4) to get satoshis/kwu. let fee_rate = { @@ -1299,11 +1245,10 @@ impl TryInto for JsonResponse { pub(crate) struct MempoolMinFeeResponse(pub FeeRate); impl TryInto for JsonResponse { - type Error = std::io::Error; - fn try_into(self) -> std::io::Result { - let fee_rate_btc_per_kvbyte = self.0["mempoolminfee"] - .as_f64() - .ok_or(std::io::Error::new(std::io::ErrorKind::Other, "Failed to parse fee rate"))?; + type Error = String; + fn try_into(self) -> Result { + let fee_rate_btc_per_kvbyte = + self.0["mempoolminfee"].as_f64().ok_or("Failed to parse fee rate".to_string())?; // Bitcoin Core gives us a feerate in BTC/KvB. // Thus, we multiply by 25_000_000 (10^8 / 4) to get satoshis/kwu. let fee_rate = { @@ -1317,22 +1262,15 @@ impl TryInto for JsonResponse { pub(crate) struct GetRawTransactionResponse(pub Transaction); impl TryInto for JsonResponse { - type Error = std::io::Error; - fn try_into(self) -> std::io::Result { + type Error = String; + fn try_into(self) -> Result { let tx = self .0 .as_str() - .ok_or(std::io::Error::new( - std::io::ErrorKind::Other, - "Failed to parse getrawtransaction response", - )) + .ok_or("Failed to parse getrawtransaction response".to_string()) .and_then(|s| { - bitcoin::consensus::encode::deserialize_hex(s).map_err(|_| { - std::io::Error::new( - std::io::ErrorKind::Other, - "Failed to parse getrawtransaction response", - ) - }) + bitcoin::consensus::encode::deserialize_hex(s) + .map_err(|_| "Failed to parse getrawtransaction response".to_string()) })?; Ok(GetRawTransactionResponse(tx)) @@ -1342,12 +1280,9 @@ impl TryInto for JsonResponse { pub struct GetRawMempoolResponse(Vec); impl TryInto for JsonResponse { - type Error = std::io::Error; - fn try_into(self) -> std::io::Result { - let res = self.0.as_array().ok_or(std::io::Error::new( - std::io::ErrorKind::Other, - "Failed to parse getrawmempool response", - ))?; + type Error = String; + fn try_into(self) -> Result { + let res = self.0.as_array().ok_or("Failed to parse getrawmempool response".to_string())?; let mut mempool_transactions = Vec::with_capacity(res.len()); @@ -1356,17 +1291,11 @@ impl TryInto for JsonResponse { match hex_str.parse::() { Ok(txid) => txid, Err(_) => { - return Err(std::io::Error::new( - std::io::ErrorKind::Other, - "Failed to parse getrawmempool response", - )); + return Err("Failed to parse getrawmempool response".to_string()); }, } } else { - return Err(std::io::Error::new( - std::io::ErrorKind::Other, - "Failed to parse getrawmempool response", - )); + return Err("Failed to parse getrawmempool response".to_string()); }; mempool_transactions.push(txid); @@ -1382,30 +1311,22 @@ pub struct GetMempoolEntryResponse { } impl TryInto for JsonResponse { - type Error = std::io::Error; - fn try_into(self) -> std::io::Result { - let res = self.0.as_object().ok_or(std::io::Error::new( - std::io::ErrorKind::Other, - "Failed to parse getmempoolentry response", - ))?; + type Error = String; + fn try_into(self) -> Result { + let res = + self.0.as_object().ok_or("Failed to parse getmempoolentry response".to_string())?; let time = match res["time"].as_u64() { Some(time) => time, None => { - return Err(std::io::Error::new( - std::io::ErrorKind::Other, - "Failed to parse getmempoolentry response", - )); + return Err("Failed to parse getmempoolentry response".to_string()); }, }; let height = match res["height"].as_u64().and_then(|h| h.try_into().ok()) { Some(height) => height, None => { - return Err(std::io::Error::new( - std::io::ErrorKind::Other, - "Failed to parse getmempoolentry response", - )); + return Err("Failed to parse getmempoolentry response".to_string()); }, }; @@ -1506,25 +1427,27 @@ pub(crate) fn rpc_credentials(rpc_user: String, rpc_password: String) -> String BASE64_STANDARD.encode(format!("{}:{}", rpc_user, rpc_password)) } -pub(crate) fn endpoint(host: String, port: u16) -> HttpEndpoint { - HttpEndpoint::for_host(host).with_port(port) +pub(crate) fn base_url(host: String, port: u16) -> String { + format!("http://{}:{}", host, port) } #[derive(Debug)] -pub struct HttpError { - pub(crate) status_code: String, - pub(crate) contents: Vec, +pub(crate) enum BitcoindClientError { + Rpc(RpcClientError), + Rest(HttpClientError), } -impl std::error::Error for HttpError {} - -impl std::fmt::Display for HttpError { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - let contents = String::from_utf8_lossy(&self.contents); - write!(f, "status_code: {}, contents: {}", self.status_code, contents) +impl fmt::Display for BitcoindClientError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Rpc(e) => write!(f, "bitcoind RPC error: {}", e), + Self::Rest(e) => write!(f, "bitcoind REST error: {}", e), + } } } +impl std::error::Error for BitcoindClientError {} + #[cfg(test)] mod tests { use bitcoin::hashes::Hash; From ff3f2f01f4f9d54fbdbf72d2f360c8be9ff8fe71 Mon Sep 17 00:00:00 2001 From: Jeffrey Czyz Date: Fri, 20 Feb 2026 11:10:29 -0600 Subject: [PATCH 3/5] Bump LDK dependency for check_closed_broadcast The macro was removed in favor of a function. --- Cargo.toml | 26 +++++++++++++------------- src/io/test_utils.rs | 13 +++++++------ 2 files changed, 20 insertions(+), 19 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 0afe8be0c..45f9d01b6 100755 --- a/Cargo.toml +++ b/Cargo.toml @@ -39,17 +39,17 @@ default = [] #lightning-liquidity = { version = "0.2.0", features = ["std"] } #lightning-macros = { version = "0.2.0" } -lightning = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "16ea4c7e2e05573bcffb3d6aa92592c24d5132ae", features = ["std"] } -lightning-types = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "16ea4c7e2e05573bcffb3d6aa92592c24d5132ae" } -lightning-invoice = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "16ea4c7e2e05573bcffb3d6aa92592c24d5132ae", features = ["std"] } -lightning-net-tokio = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "16ea4c7e2e05573bcffb3d6aa92592c24d5132ae" } -lightning-persister = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "16ea4c7e2e05573bcffb3d6aa92592c24d5132ae", features = ["tokio"] } -lightning-background-processor = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "16ea4c7e2e05573bcffb3d6aa92592c24d5132ae" } -lightning-rapid-gossip-sync = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "16ea4c7e2e05573bcffb3d6aa92592c24d5132ae" } -lightning-block-sync = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "16ea4c7e2e05573bcffb3d6aa92592c24d5132ae", features = ["rest-client", "rpc-client", "tokio"] } -lightning-transaction-sync = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "16ea4c7e2e05573bcffb3d6aa92592c24d5132ae", features = ["esplora-async-https", "time", "electrum-rustls-ring"] } -lightning-liquidity = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "16ea4c7e2e05573bcffb3d6aa92592c24d5132ae", features = ["std"] } -lightning-macros = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "16ea4c7e2e05573bcffb3d6aa92592c24d5132ae" } +lightning = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "2203e2dbb603850b83c986da14c0678a04bad11f", features = ["std"] } +lightning-types = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "2203e2dbb603850b83c986da14c0678a04bad11f" } +lightning-invoice = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "2203e2dbb603850b83c986da14c0678a04bad11f", features = ["std"] } +lightning-net-tokio = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "2203e2dbb603850b83c986da14c0678a04bad11f" } +lightning-persister = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "2203e2dbb603850b83c986da14c0678a04bad11f", features = ["tokio"] } +lightning-background-processor = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "2203e2dbb603850b83c986da14c0678a04bad11f" } +lightning-rapid-gossip-sync = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "2203e2dbb603850b83c986da14c0678a04bad11f" } +lightning-block-sync = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "2203e2dbb603850b83c986da14c0678a04bad11f", features = ["rest-client", "rpc-client", "tokio"] } +lightning-transaction-sync = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "2203e2dbb603850b83c986da14c0678a04bad11f", features = ["esplora-async-https", "time", "electrum-rustls-ring"] } +lightning-liquidity = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "2203e2dbb603850b83c986da14c0678a04bad11f", features = ["std"] } +lightning-macros = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "2203e2dbb603850b83c986da14c0678a04bad11f" } bdk_chain = { version = "0.23.0", default-features = false, features = ["std"] } bdk_esplora = { version = "0.22.0", default-features = false, features = ["async-https-rustls", "tokio"]} @@ -78,13 +78,13 @@ log = { version = "0.4.22", default-features = false, features = ["std"]} vss-client = { package = "vss-client-ng", version = "0.5" } prost = { version = "0.11.6", default-features = false} #bitcoin-payment-instructions = { version = "0.6" } -bitcoin-payment-instructions = { git = "https://github.com/jkczyz/bitcoin-payment-instructions", rev = "3daaed987e0948e9c82c233aec268c7b84d78eea" } +bitcoin-payment-instructions = { git = "https://github.com/jkczyz/bitcoin-payment-instructions", rev = "d3d1f9fd6d59ad901834af5cc2d2d44743d5f28e" } [target.'cfg(windows)'.dependencies] winapi = { version = "0.3", features = ["winbase"] } [dev-dependencies] -lightning = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "16ea4c7e2e05573bcffb3d6aa92592c24d5132ae", features = ["std", "_test_utils"] } +lightning = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "2203e2dbb603850b83c986da14c0678a04bad11f", features = ["std", "_test_utils"] } rand = { version = "0.9.2", default-features = false, features = ["std", "thread_rng", "os_rng"] } proptest = "1.0.0" regex = "1.5.6" diff --git a/src/io/test_utils.rs b/src/io/test_utils.rs index 9add2d6c1..88078b316 100644 --- a/src/io/test_utils.rs +++ b/src/io/test_utils.rs @@ -12,16 +12,17 @@ use std::path::PathBuf; use std::sync::Mutex; use lightning::events::ClosureReason; +use lightning::io; use lightning::ln::functional_test_utils::{ - check_added_monitors, check_closed_event, connect_block, create_announced_chan_between_nodes, - create_chanmon_cfgs, create_dummy_block, create_network, create_node_cfgs, - create_node_chanmgrs, send_payment, test_legacy_channel_config, TestChanMonCfg, + check_added_monitors, check_closed_broadcast, check_closed_event, connect_block, + create_announced_chan_between_nodes, create_chanmon_cfgs, create_dummy_block, create_network, + create_node_cfgs, create_node_chanmgrs, send_payment, test_legacy_channel_config, + TestChanMonCfg, }; use lightning::util::persist::{ KVStore, KVStoreSync, MonitorUpdatingPersister, KVSTORE_NAMESPACE_KEY_MAX_LEN, }; use lightning::util::test_utils; -use lightning::{check_closed_broadcast, io}; use rand::distr::Alphanumeric; use rand::{rng, Rng}; @@ -334,7 +335,7 @@ pub(crate) fn do_test_store(store_0: &K, store_1: &K) { &[nodes[1].node.get_our_node_id()], 100000, ); - check_closed_broadcast!(nodes[0], true); + check_closed_broadcast(&nodes[0], 1, true); check_added_monitors(&nodes[0], 1); let node_txn = nodes[0].tx_broadcaster.txn_broadcast(); @@ -343,7 +344,7 @@ pub(crate) fn do_test_store(store_0: &K, store_1: &K) { let dummy_block = create_dummy_block(nodes[0].best_block_hash(), 42, txn); connect_block(&nodes[1], &dummy_block); - check_closed_broadcast!(nodes[1], true); + check_closed_broadcast(&nodes[1], 1, true); let reason = ClosureReason::CommitmentTxConfirmed; let node_id_0 = nodes[0].node.get_our_node_id(); check_closed_event(&nodes[1], 1, reason, &[node_id_0], 100000); From 62dca02851b213034a9e6989b466753cd238a532 Mon Sep 17 00:00:00 2001 From: Jeffrey Czyz Date: Fri, 20 Feb 2026 10:30:59 -0600 Subject: [PATCH 4/5] Bump LDK dependency for wallet_utils refactor Some types previously existing in lightning::events::bump_transaction have been moved to lightning::util::wallet_utils. --- Cargo.toml | 26 +++++++++++++------------- src/lib.rs | 2 +- src/types.rs | 2 +- src/wallet/mod.rs | 6 +++--- 4 files changed, 18 insertions(+), 18 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 45f9d01b6..c96e610af 100755 --- a/Cargo.toml +++ b/Cargo.toml @@ -39,17 +39,17 @@ default = [] #lightning-liquidity = { version = "0.2.0", features = ["std"] } #lightning-macros = { version = "0.2.0" } -lightning = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "2203e2dbb603850b83c986da14c0678a04bad11f", features = ["std"] } -lightning-types = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "2203e2dbb603850b83c986da14c0678a04bad11f" } -lightning-invoice = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "2203e2dbb603850b83c986da14c0678a04bad11f", features = ["std"] } -lightning-net-tokio = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "2203e2dbb603850b83c986da14c0678a04bad11f" } -lightning-persister = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "2203e2dbb603850b83c986da14c0678a04bad11f", features = ["tokio"] } -lightning-background-processor = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "2203e2dbb603850b83c986da14c0678a04bad11f" } -lightning-rapid-gossip-sync = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "2203e2dbb603850b83c986da14c0678a04bad11f" } -lightning-block-sync = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "2203e2dbb603850b83c986da14c0678a04bad11f", features = ["rest-client", "rpc-client", "tokio"] } -lightning-transaction-sync = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "2203e2dbb603850b83c986da14c0678a04bad11f", features = ["esplora-async-https", "time", "electrum-rustls-ring"] } -lightning-liquidity = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "2203e2dbb603850b83c986da14c0678a04bad11f", features = ["std"] } -lightning-macros = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "2203e2dbb603850b83c986da14c0678a04bad11f" } +lightning = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "8bbc5ec0e4fd27071576374981d5d2b2ac18376c", features = ["std"] } +lightning-types = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "8bbc5ec0e4fd27071576374981d5d2b2ac18376c" } +lightning-invoice = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "8bbc5ec0e4fd27071576374981d5d2b2ac18376c", features = ["std"] } +lightning-net-tokio = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "8bbc5ec0e4fd27071576374981d5d2b2ac18376c" } +lightning-persister = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "8bbc5ec0e4fd27071576374981d5d2b2ac18376c", features = ["tokio"] } +lightning-background-processor = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "8bbc5ec0e4fd27071576374981d5d2b2ac18376c" } +lightning-rapid-gossip-sync = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "8bbc5ec0e4fd27071576374981d5d2b2ac18376c" } +lightning-block-sync = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "8bbc5ec0e4fd27071576374981d5d2b2ac18376c", features = ["rest-client", "rpc-client", "tokio"] } +lightning-transaction-sync = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "8bbc5ec0e4fd27071576374981d5d2b2ac18376c", features = ["esplora-async-https", "time", "electrum-rustls-ring"] } +lightning-liquidity = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "8bbc5ec0e4fd27071576374981d5d2b2ac18376c", features = ["std"] } +lightning-macros = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "8bbc5ec0e4fd27071576374981d5d2b2ac18376c" } bdk_chain = { version = "0.23.0", default-features = false, features = ["std"] } bdk_esplora = { version = "0.22.0", default-features = false, features = ["async-https-rustls", "tokio"]} @@ -78,13 +78,13 @@ log = { version = "0.4.22", default-features = false, features = ["std"]} vss-client = { package = "vss-client-ng", version = "0.5" } prost = { version = "0.11.6", default-features = false} #bitcoin-payment-instructions = { version = "0.6" } -bitcoin-payment-instructions = { git = "https://github.com/jkczyz/bitcoin-payment-instructions", rev = "d3d1f9fd6d59ad901834af5cc2d2d44743d5f28e" } +bitcoin-payment-instructions = { git = "https://github.com/jkczyz/bitcoin-payment-instructions", rev = "48f409bc1c2c140dfdb5514125ef7d714110b7f0" } [target.'cfg(windows)'.dependencies] winapi = { version = "0.3", features = ["winbase"] } [dev-dependencies] -lightning = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "2203e2dbb603850b83c986da14c0678a04bad11f", features = ["std", "_test_utils"] } +lightning = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "8bbc5ec0e4fd27071576374981d5d2b2ac18376c", features = ["std", "_test_utils"] } rand = { version = "0.9.2", default-features = false, features = ["std", "thread_rng", "os_rng"] } proptest = "1.0.0" regex = "1.5.6" diff --git a/src/lib.rs b/src/lib.rs index 320f1662a..1b93cb6e9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -138,7 +138,6 @@ use gossip::GossipSource; use graph::NetworkGraph; use io::utils::write_node_metrics; use lightning::chain::BestBlock; -use lightning::events::bump_transaction::Wallet as LdkWallet; use lightning::impl_writeable_tlv_based; use lightning::ln::channel_state::{ChannelDetails as LdkChannelDetails, ChannelShutdownState}; use lightning::ln::channelmanager::PaymentId; @@ -146,6 +145,7 @@ use lightning::ln::msgs::SocketAddress; use lightning::routing::gossip::NodeAlias; use lightning::sign::EntropySource; use lightning::util::persist::KVStoreSync; +use lightning::util::wallet_utils::Wallet as LdkWallet; use lightning_background_processor::process_events_async; use liquidity::{LSPS1Liquidity, LiquiditySource}; use logger::{log_debug, log_error, log_info, log_trace, LdkLogger, Logger}; diff --git a/src/types.rs b/src/types.rs index b5b1ffed7..c5ff07756 100644 --- a/src/types.rs +++ b/src/types.rs @@ -314,7 +314,7 @@ pub(crate) type Sweeper = OutputSweeper< pub(crate) type BumpTransactionEventHandler = lightning::events::bump_transaction::BumpTransactionEventHandler< Arc, - Arc, Arc>>, + Arc, Arc>>, Arc, Arc, >; diff --git a/src/wallet/mod.rs b/src/wallet/mod.rs index c4e36b83c..dd88dad90 100644 --- a/src/wallet/mod.rs +++ b/src/wallet/mod.rs @@ -36,9 +36,6 @@ use lightning::chain::chaininterface::{ }; use lightning::chain::channelmonitor::ANTI_REORG_DELAY; use lightning::chain::{BestBlock, ClaimId, Listen}; -use lightning::events::bump_transaction::{ - CoinSelection, CoinSelectionSource, Input, Utxo, WalletSource, -}; use lightning::ln::channelmanager::PaymentId; use lightning::ln::funding::FundingTxInput; use lightning::ln::inbound_payment::ExpandedKey; @@ -49,6 +46,9 @@ use lightning::sign::{ PeerStorageKey, Recipient, SignerProvider, SpendableOutputDescriptor, }; use lightning::util::message_signing; +use lightning::util::wallet_utils::{ + CoinSelection, CoinSelectionSource, Input, Utxo, WalletSource, +}; use lightning_invoice::RawBolt11Invoice; use persist::KVStoreWalletPersister; From 74976e3a89d363a28025e3eb000a9d7c589db283 Mon Sep 17 00:00:00 2001 From: Jeffrey Czyz Date: Mon, 23 Feb 2026 17:36:40 -0600 Subject: [PATCH 5/5] Bump LDK dependency for v1::FilesystemStore --- Cargo.toml | 26 +++++++++++++------------- src/builder.rs | 2 +- tests/common/mod.rs | 2 +- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index c96e610af..e98c21ab4 100755 --- a/Cargo.toml +++ b/Cargo.toml @@ -39,17 +39,17 @@ default = [] #lightning-liquidity = { version = "0.2.0", features = ["std"] } #lightning-macros = { version = "0.2.0" } -lightning = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "8bbc5ec0e4fd27071576374981d5d2b2ac18376c", features = ["std"] } -lightning-types = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "8bbc5ec0e4fd27071576374981d5d2b2ac18376c" } -lightning-invoice = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "8bbc5ec0e4fd27071576374981d5d2b2ac18376c", features = ["std"] } -lightning-net-tokio = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "8bbc5ec0e4fd27071576374981d5d2b2ac18376c" } -lightning-persister = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "8bbc5ec0e4fd27071576374981d5d2b2ac18376c", features = ["tokio"] } -lightning-background-processor = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "8bbc5ec0e4fd27071576374981d5d2b2ac18376c" } -lightning-rapid-gossip-sync = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "8bbc5ec0e4fd27071576374981d5d2b2ac18376c" } -lightning-block-sync = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "8bbc5ec0e4fd27071576374981d5d2b2ac18376c", features = ["rest-client", "rpc-client", "tokio"] } -lightning-transaction-sync = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "8bbc5ec0e4fd27071576374981d5d2b2ac18376c", features = ["esplora-async-https", "time", "electrum-rustls-ring"] } -lightning-liquidity = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "8bbc5ec0e4fd27071576374981d5d2b2ac18376c", features = ["std"] } -lightning-macros = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "8bbc5ec0e4fd27071576374981d5d2b2ac18376c" } +lightning = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "2d2151a4e40734b871289e8bb0fa13466c0ea60a", features = ["std"] } +lightning-types = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "2d2151a4e40734b871289e8bb0fa13466c0ea60a" } +lightning-invoice = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "2d2151a4e40734b871289e8bb0fa13466c0ea60a", features = ["std"] } +lightning-net-tokio = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "2d2151a4e40734b871289e8bb0fa13466c0ea60a" } +lightning-persister = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "2d2151a4e40734b871289e8bb0fa13466c0ea60a", features = ["tokio"] } +lightning-background-processor = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "2d2151a4e40734b871289e8bb0fa13466c0ea60a" } +lightning-rapid-gossip-sync = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "2d2151a4e40734b871289e8bb0fa13466c0ea60a" } +lightning-block-sync = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "2d2151a4e40734b871289e8bb0fa13466c0ea60a", features = ["rest-client", "rpc-client", "tokio"] } +lightning-transaction-sync = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "2d2151a4e40734b871289e8bb0fa13466c0ea60a", features = ["esplora-async-https", "time", "electrum-rustls-ring"] } +lightning-liquidity = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "2d2151a4e40734b871289e8bb0fa13466c0ea60a", features = ["std"] } +lightning-macros = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "2d2151a4e40734b871289e8bb0fa13466c0ea60a" } bdk_chain = { version = "0.23.0", default-features = false, features = ["std"] } bdk_esplora = { version = "0.22.0", default-features = false, features = ["async-https-rustls", "tokio"]} @@ -78,13 +78,13 @@ log = { version = "0.4.22", default-features = false, features = ["std"]} vss-client = { package = "vss-client-ng", version = "0.5" } prost = { version = "0.11.6", default-features = false} #bitcoin-payment-instructions = { version = "0.6" } -bitcoin-payment-instructions = { git = "https://github.com/jkczyz/bitcoin-payment-instructions", rev = "48f409bc1c2c140dfdb5514125ef7d714110b7f0" } +bitcoin-payment-instructions = { git = "https://github.com/jkczyz/bitcoin-payment-instructions", rev = "8fbb1997917c9a5687f8e532cab9a453af3c3dc1" } [target.'cfg(windows)'.dependencies] winapi = { version = "0.3", features = ["winbase"] } [dev-dependencies] -lightning = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "8bbc5ec0e4fd27071576374981d5d2b2ac18376c", features = ["std", "_test_utils"] } +lightning = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "2d2151a4e40734b871289e8bb0fa13466c0ea60a", features = ["std", "_test_utils"] } rand = { version = "0.9.2", default-features = false, features = ["std", "thread_rng", "os_rng"] } proptest = "1.0.0" regex = "1.5.6" diff --git a/src/builder.rs b/src/builder.rs index 7a285876f..a2ea9aea7 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -39,7 +39,7 @@ use lightning::util::persist::{ }; use lightning::util::ser::ReadableArgs; use lightning::util::sweep::OutputSweeper; -use lightning_persister::fs_store::FilesystemStore; +use lightning_persister::fs_store::v1::FilesystemStore; use vss_client::headers::VssHeaderProvider; use crate::chain::ChainSource; diff --git a/tests/common/mod.rs b/tests/common/mod.rs index 0413b4552..c75a6947c 100644 --- a/tests/common/mod.rs +++ b/tests/common/mod.rs @@ -39,7 +39,7 @@ use lightning::routing::gossip::NodeAlias; use lightning::util::persist::{KVStore, KVStoreSync}; use lightning::util::test_utils::TestStore; use lightning_invoice::{Bolt11InvoiceDescription, Description}; -use lightning_persister::fs_store::FilesystemStore; +use lightning_persister::fs_store::v1::FilesystemStore; use lightning_types::payment::{PaymentHash, PaymentPreimage}; use logging::TestLogWriter; use rand::distr::Alphanumeric;