From 9df5f83b1fb856894023d028200cc8be9181d41f Mon Sep 17 00:00:00 2001 From: Wilmer Paulino Date: Wed, 29 Apr 2026 11:50:54 -0700 Subject: [PATCH] Support async signing of splice shared input While user signatures may be provided whenever ready at the user's discretion when handling a `FundingTransactionReadyForSigning` event, it does not cover the user's signature for the 2-of-2 multisig input in a splice. This signature is obtained via the `EcdsaChannelSigner`, which did not support providing it asynchronously. Since the splice shared input signature is part of the `tx_signatures` message, we're not allowed to send the message until it's complete. This results in us needing to explicitly handle the signature exchange logic when the signer unblocks the shared input signature. --- lightning/src/ln/async_signer_tests.rs | 77 ++++++++++++++ lightning/src/ln/channel.rs | 118 +++++++++++++++++----- lightning/src/ln/channelmanager.rs | 71 +++++++++++-- lightning/src/ln/interactivetxs.rs | 61 +++++++---- lightning/src/sign/ecdsa.rs | 8 +- lightning/src/sign/mod.rs | 4 +- lightning/src/util/dyn_signer.rs | 2 +- lightning/src/util/test_channel_signer.rs | 8 +- 8 files changed, 294 insertions(+), 55 deletions(-) diff --git a/lightning/src/ln/async_signer_tests.rs b/lightning/src/ln/async_signer_tests.rs index f238c1db060..ccf49e8e815 100644 --- a/lightning/src/ln/async_signer_tests.rs +++ b/lightning/src/ln/async_signer_tests.rs @@ -1742,3 +1742,80 @@ fn test_async_splice_initial_commit_sig_waits_for_monitor_before_tx_signatures() let _ = get_event!(initiator, Event::SplicePending); let _ = get_event!(acceptor, Event::SplicePending); } + +#[test] +fn test_async_splice_shared_input_signature_released_on_unblock() { + // Test that we can provide the signature of a splice's shared input asynchronously, and check + // that the holding cell is freed after exiting quiescence due to exchanging `tx_signatures`. + let chanmon_cfgs = create_chanmon_cfgs(2); + let node_cfgs = create_node_cfgs(2, &chanmon_cfgs); + let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]); + let nodes = create_network(2, &node_cfgs, &node_chanmgrs); + + let channel_id = create_announced_chan_between_nodes(&nodes, 0, 1).2; + + let (initiator, acceptor) = (&nodes[0], &nodes[1]); + let initiator_node_id = initiator.node.get_our_node_id(); + let acceptor_node_id = acceptor.node.get_our_node_id(); + + initiator.disable_channel_signer_op( + &acceptor_node_id, + &channel_id, + SignerOp::SignSpliceSharedInput, + ); + + let outputs = vec![TxOut { + value: Amount::from_sat(1_000), + script_pubkey: nodes[0].wallet_source.get_change_script().unwrap(), + }]; + let contribution = initiate_splice_out(initiator, acceptor, channel_id, outputs).unwrap(); + negotiate_splice_tx(initiator, acceptor, channel_id, contribution); + + let event = get_event!(initiator, Event::FundingTransactionReadyForSigning); + if let Event::FundingTransactionReadyForSigning { unsigned_transaction, .. } = event { + let partially_signed_tx = initiator.wallet_source.sign_tx(unsigned_transaction).unwrap(); + initiator + .node + .funding_transaction_signed(&channel_id, &acceptor_node_id, partially_signed_tx) + .unwrap(); + } + + let initiator_commit_sig = get_htlc_update_msgs(initiator, &acceptor_node_id); + acceptor + .node + .handle_commitment_signed(initiator_node_id, &initiator_commit_sig.commitment_signed[0]); + check_added_monitors(acceptor, 1); + + let acceptor_msg_events = acceptor.node.get_and_clear_pending_msg_events(); + assert_eq!(acceptor_msg_events.len(), 2, "{acceptor_msg_events:?}"); + for msg_event in &acceptor_msg_events { + match msg_event { + MessageSendEvent::UpdateHTLCs { updates, .. } => { + initiator + .node + .handle_commitment_signed(acceptor_node_id, &updates.commitment_signed[0]); + check_added_monitors(initiator, 1); + }, + MessageSendEvent::SendTxSignatures { msg, .. } => { + initiator.node.handle_tx_signatures(acceptor_node_id, msg); + }, + _ => panic!("Unexpected event"), + } + } + + assert!(initiator.node.get_and_clear_pending_msg_events().is_empty()); + + initiator.enable_channel_signer_op( + &acceptor_node_id, + &channel_id, + SignerOp::SignSpliceSharedInput, + ); + initiator.node.signer_unblocked(None); + + let tx_signatures = + get_event_msg!(initiator, MessageSendEvent::SendTxSignatures, acceptor_node_id); + acceptor.node.handle_tx_signatures(initiator_node_id, &tx_signatures); + + let _ = get_event!(initiator, Event::SplicePending); + let _ = get_event!(acceptor, Event::SplicePending); +} diff --git a/lightning/src/ln/channel.rs b/lightning/src/ln/channel.rs index e6397aefbcb..92f5d116f4c 100644 --- a/lightning/src/ln/channel.rs +++ b/lightning/src/ln/channel.rs @@ -1205,8 +1205,7 @@ pub(super) struct SignerResumeUpdates { pub accept_channel: Option, pub funding_created: Option, pub funding_signed: Option, - pub funding_commit_sig: Option, - pub tx_signatures: Option, + pub funding_tx_signed: Option, pub channel_ready: Option, pub order: RAACommitmentOrder, pub closing_signed: Option, @@ -1639,11 +1638,11 @@ where #[rustfmt::skip] pub fn signer_maybe_unblocked( - &mut self, chain_hash: ChainHash, logger: &L, path_for_release_htlc: CBP + &mut self, chain_hash: ChainHash, best_block_height: u32, logger: &L, path_for_release_htlc: CBP ) -> Result, ChannelError> where CBP: Fn(u64) -> BlindedMessagePath { match &mut self.phase { ChannelPhase::Undefined => unreachable!(), - ChannelPhase::Funded(chan) => chan.signer_maybe_unblocked(logger, path_for_release_htlc).map(|r| Some(r)), + ChannelPhase::Funded(chan) => chan.signer_maybe_unblocked(best_block_height, logger, path_for_release_htlc).map(|r| Some(r)), ChannelPhase::UnfundedOutboundV1(chan) => { let (open_channel, funding_created) = chan.signer_maybe_unblocked(chain_hash, logger); Ok(Some(SignerResumeUpdates { @@ -1653,8 +1652,7 @@ where accept_channel: None, funding_created, funding_signed: None, - funding_commit_sig: None, - tx_signatures: None, + funding_tx_signed: None, channel_ready: None, order: chan.context.resend_order.clone(), closing_signed: None, @@ -1671,8 +1669,7 @@ where accept_channel, funding_created: None, funding_signed: None, - funding_commit_sig: None, - tx_signatures: None, + funding_tx_signed: None, channel_ready: None, order: chan.context.resend_order.clone(), closing_signed: None, @@ -2213,17 +2210,29 @@ where let shared_input_signature = if let Some(splice_input_index) = signing_session.unsigned_tx().shared_input_index() { - let sig = context.holder_signer.sign_splice_shared_input( - &funding.channel_transaction_parameters, - tx, - splice_input_index as usize, - &context.secp_ctx, - ); - Some(sig) + let sig = context + .holder_signer + .sign_splice_shared_input( + &funding.channel_transaction_parameters, + tx, + splice_input_index as usize, + &context.secp_ctx, + ) + .ok(); + if sig.is_none() { + log_debug!( + logger, + "Splice shared input signature not available, waiting on async signer" + ); + } + sig } else { None }; - debug_assert_eq!(pending_splice.is_some(), shared_input_signature.is_some()); + debug_assert_eq!( + pending_splice.is_some(), + signing_session.unsigned_tx().shared_input_index().is_some() + ); let tx_signatures = msgs::TxSignatures { channel_id: context.channel_id, @@ -9450,6 +9459,8 @@ where } } + let awaiting_holder_shared_input_signature = + signing_session.awaiting_holder_shared_input_signature(); let (holder_tx_signatures, funding_tx) = signing_session.received_tx_signatures(msg).map_err(|msg| ChannelError::Warn(msg))?; @@ -9488,6 +9499,11 @@ where best_block_height, &logger, ); + } else if awaiting_holder_shared_input_signature { + log_debug!( + logger, + "Waiting for funding transaction shared input signature before finalizing negotiation" + ); } else { debug_assert!( false, @@ -9882,7 +9898,7 @@ where /// blocked. #[rustfmt::skip] pub fn signer_maybe_unblocked( - &mut self, logger: &L, path_for_release_htlc: CBP + &mut self, best_block_height: u32, logger: &L, path_for_release_htlc: CBP ) -> Result where CBP: Fn(u64) -> BlindedMessagePath { if let Some((commitment_number, commitment_secret)) = self.context.signer_pending_stale_state_verification.clone() { if let Ok(expected_point) = self @@ -9938,16 +9954,65 @@ where None }; - let tx_signatures = if funding_commit_sig.is_some() { + let mut shared_input_signature_unblocked = false; + { + if let Some(signing_session) = self.context.interactive_tx_signing_session.as_mut() { + if signing_session.awaiting_holder_shared_input_signature() { + let splice_input_index = signing_session + .unsigned_tx() + .shared_input_index() + .expect("Missing shared input index while awaiting a splice signature"); + log_trace!(logger, "Attempting to generate pending splice shared input signature..."); + if let Ok(shared_input_signature) = self.context.holder_signer.sign_splice_shared_input( + &self.funding.channel_transaction_parameters, + signing_session.unsigned_tx().tx(), + splice_input_index as usize, + &self.context.secp_ctx, + ) { + shared_input_signature_unblocked = true; + signing_session + .provide_holder_shared_input_signature(shared_input_signature) + .map_err(ChannelError::close)?; + } + } + } + } + + let mut tx_signatures = None; + let mut funding_tx = None; + if funding_commit_sig.is_some() || shared_input_signature_unblocked { if let Some(signing_session) = self.context.interactive_tx_signing_session.as_ref() { - signing_session.holder_tx_signatures().filter(|_| !self.is_awaiting_monitor_update()) + if !self.is_awaiting_monitor_update() && !self.context.signer_pending_funding { + tx_signatures = signing_session.holder_tx_signatures(); + funding_tx = tx_signatures.as_ref().and_then(|_| signing_session.signed_tx()); + } } else { debug_assert!(false); - None } - } else { - None - }; + } + + let mut funding_tx_signed = None; + if funding_commit_sig.is_some() || tx_signatures.is_some() || funding_tx.is_some() { + let mut resumed = FundingTxSigned { + commitment_signed: funding_commit_sig, + counterparty_initial_commitment_signed_result: None, + tx_signatures, + funding_tx: None, + splice_negotiated: None, + splice_locked: None, + }; + if let Some(funding_tx) = funding_tx { + let funding_logger = WithChannelContext::from(logger, &self.context, None); + debug_assert!(resumed.tx_signatures.is_some()); + self.on_tx_signatures_exchange( + &mut resumed, + funding_tx, + best_block_height, + &funding_logger, + ); + } + funding_tx_signed = Some(resumed); + } // Provide a `channel_ready` message if we need to, but only if we're _not_ still pending // funding. @@ -10013,8 +10078,8 @@ where if revoke_and_ack.is_some() { "a" } else { "no" }, self.context.resend_order, if funding_signed.is_some() { "a" } else { "no" }, - if funding_commit_sig.is_some() { "a" } else { "no" }, - if tx_signatures.is_some() { "a" } else { "no" }, + if funding_tx_signed.as_ref().map(|v| v.commitment_signed.is_some()).unwrap_or(false) { "a" } else { "no" }, + if funding_tx_signed.as_ref().map(|v| v.tx_signatures.is_some()).unwrap_or(false) { "a" } else { "no" }, if channel_ready.is_some() { "a" } else { "no" }, if closing_signed.is_some() { "a" } else { "no" }, if signed_closing_tx.is_some() { "a" } else { "no" }, @@ -10027,8 +10092,7 @@ where accept_channel: None, funding_created: None, funding_signed, - funding_commit_sig, - tx_signatures, + funding_tx_signed, channel_ready, order: self.context.resend_order.clone(), closing_signed, diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs index 1f32423507f..54317fd7bb2 100644 --- a/lightning/src/ln/channelmanager.rs +++ b/lightning/src/ln/channelmanager.rs @@ -13887,20 +13887,24 @@ This indicates a bug inside LDK. Please report this error at https://github.com/ /// [`ChannelSigner`]: crate::sign::ChannelSigner pub fn signer_unblocked(&self, channel_opt: Option<(PublicKey, ChannelId)>) { let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self); + let mut needs_holding_cell_release = false; // Returns whether we should remove this channel as it's just been closed. let unblock_chan = |chan: &mut Channel, - pending_msg_events: &mut Vec| + pending_msg_events: &mut Vec, + needs_holding_cell_release: &mut bool| -> Result, ChannelError> { let channel_id = chan.context().channel_id(); let outbound_scid_alias = chan.context().outbound_scid_alias(); let logger = WithChannelContext::from(&self.logger, &chan.context(), None); let node_id = chan.context().get_counterparty_node_id(); + let best_block_height = self.best_block.read().unwrap().height; let cbp = |htlc_id| { self.path_for_release_held_htlc(htlc_id, outbound_scid_alias, &channel_id, &node_id) }; - let msgs = chan.signer_maybe_unblocked(self.chain_hash, &&logger, cbp)?; - if let Some(msgs) = msgs { + let msgs = + chan.signer_maybe_unblocked(self.chain_hash, best_block_height, &&logger, cbp)?; + if let Some(mut msgs) = msgs { if chan.context().is_connected() { if let Some(msg) = msgs.open_channel { pending_msg_events.push(MessageSendEvent::SendOpenChannel { node_id, msg }); @@ -13940,7 +13944,11 @@ This indicates a bug inside LDK. Please report this error at https://github.com/ pending_msg_events .push(MessageSendEvent::SendFundingSigned { node_id, msg }); } - if let Some(msg) = msgs.funding_commit_sig { + if let Some(msg) = msgs + .funding_tx_signed + .as_mut() + .and_then(|funding_tx_signed| funding_tx_signed.commitment_signed.take()) + { pending_msg_events.push(MessageSendEvent::UpdateHTLCs { node_id, channel_id, @@ -13954,7 +13962,11 @@ This indicates a bug inside LDK. Please report this error at https://github.com/ }, }); } - if let Some(msg) = msgs.tx_signatures { + if let Some(msg) = msgs + .funding_tx_signed + .as_mut() + .and_then(|funding_tx_signed| funding_tx_signed.tx_signatures.take()) + { pending_msg_events .push(MessageSendEvent::SendTxSignatures { node_id, msg }); } @@ -13967,6 +13979,45 @@ This indicates a bug inside LDK. Please report this error at https://github.com/ if let Some(msg) = msgs.channel_ready { self.send_channel_ready(pending_msg_events, funded_chan, msg); } + if let Some(msg) = msgs + .funding_tx_signed + .as_mut() + .and_then(|funding_tx_signed| funding_tx_signed.splice_locked.take()) + { + pending_msg_events + .push(MessageSendEvent::SendSpliceLocked { node_id, msg }); + } + if let Some((splice_tx, tx_type)) = msgs + .funding_tx_signed + .as_mut() + .and_then(|funding_tx_signed| funding_tx_signed.funding_tx.take()) + { + debug_assert!(matches!(tx_type, TransactionType::Splice { .. })); + log_info!( + logger, + "Broadcasting signed splice transaction with txid {}", + splice_tx.compute_txid(), + ); + self.tx_broadcaster.broadcast_transactions(&[(&splice_tx, tx_type)]); + } + if let Some(splice_negotiated) = msgs + .funding_tx_signed + .as_mut() + .and_then(|funding_tx_signed| funding_tx_signed.splice_negotiated.take()) + { + *needs_holding_cell_release = true; + self.pending_events.lock().unwrap().push_back(( + events::Event::SplicePending { + channel_id, + counterparty_node_id: node_id, + user_channel_id: funded_chan.context.get_user_id(), + new_funding_txo: splice_negotiated.funding_txo, + channel_type: splice_negotiated.channel_type, + new_funding_redeem_script: splice_negotiated.funding_redeem_script, + }, + None, + )); + } if let Some(broadcast_tx) = msgs.signed_closing_tx { log_info!(logger, "Broadcasting closing tx {}", log_tx!(broadcast_tx)); self.tx_broadcaster.broadcast_transactions(&[( @@ -13981,6 +14032,7 @@ This indicates a bug inside LDK. Please report this error at https://github.com/ // We don't know how to handle a channel_ready or signed_closing_tx for a // non-funded channel. debug_assert!(msgs.channel_ready.is_none()); + debug_assert!(msgs.funding_tx_signed.is_none()); debug_assert!(msgs.signed_closing_tx.is_none()); } Ok(msgs.shutdown_result) @@ -14004,7 +14056,11 @@ This indicates a bug inside LDK. Please report this error at https://github.com/ peer_state.channel_by_id.retain(|_, chan| { let shutdown_result = match channel_opt { Some((_, channel_id)) if chan.context().channel_id() != channel_id => None, - _ => match unblock_chan(chan, &mut peer_state.pending_msg_events) { + _ => match unblock_chan( + chan, + &mut peer_state.pending_msg_events, + &mut needs_holding_cell_release, + ) { Ok(shutdown_result) => shutdown_result, Err(err) => { let (_, err) = self.locked_handle_force_close( @@ -14045,6 +14101,9 @@ This indicates a bug inside LDK. Please report this error at https://github.com/ }); } drop(per_peer_state); + if needs_holding_cell_release { + self.check_free_holding_cells(); + } for (err, counterparty_node_id) in shutdown_results { let _ = self.handle_error(err, counterparty_node_id); } diff --git a/lightning/src/ln/interactivetxs.rs b/lightning/src/ln/interactivetxs.rs index ca8c4450012..3232b37599d 100644 --- a/lightning/src/ln/interactivetxs.rs +++ b/lightning/src/ln/interactivetxs.rs @@ -18,6 +18,7 @@ use bitcoin::constants::WITNESS_SCALE_FACTOR; use bitcoin::ecdsa::Signature as BitcoinSignature; use bitcoin::key::Secp256k1; use bitcoin::policy::MAX_STANDARD_TX_WEIGHT; +use bitcoin::secp256k1::ecdsa::Signature; use bitcoin::secp256k1::{Message, PublicKey}; use bitcoin::sighash::SighashCache; use bitcoin::transaction::Version; @@ -472,12 +473,12 @@ impl ConstructedTransaction { } fn finalize( - &self, holder_tx_signatures: &TxSignatures, counterparty_tx_signatures: &TxSignatures, - shared_input_sig: Option<&SharedInputSignature>, + &self, holder_tx_signatures: TxSignatures, counterparty_tx_signatures: TxSignatures, + shared_input_sig: Option, ) -> Option { let mut tx = self.tx.clone(); - self.add_local_witnesses(&mut tx, holder_tx_signatures.witnesses.clone()); - self.add_remote_witnesses(&mut tx, counterparty_tx_signatures.witnesses.clone()); + self.add_local_witnesses(&mut tx, holder_tx_signatures.witnesses); + self.add_remote_witnesses(&mut tx, counterparty_tx_signatures.witnesses); if let Some(shared_input_index) = self.shared_input_index { let holder_shared_input_sig = @@ -612,9 +613,21 @@ impl InteractiveTxSigningSession { self.holder_tx_signatures.is_some() } + pub fn awaiting_holder_shared_input_signature(&self) -> bool { + self.holder_tx_signatures + .as_ref() + .map(|tx_signatures| { + self.shared_input().is_some() && tx_signatures.shared_input_signature.is_none() + }) + .unwrap_or(false) + } + pub fn holder_tx_signatures(&self) -> Option { self.holder_tx_signatures .as_ref() + .filter(|tx_signatures| { + self.shared_input().is_none() || tx_signatures.shared_input_signature.is_some() + }) .filter(|_| { (self.has_received_commitment_signed && self.holder_sends_tx_signatures_first) || self.has_received_tx_signatures() @@ -655,11 +668,8 @@ impl InteractiveTxSigningSession { self.counterparty_tx_signatures = Some(tx_signatures.clone()); - let holder_tx_signatures = if !self.holder_sends_tx_signatures_first { - self.holder_tx_signatures.clone() - } else { - None - }; + let holder_tx_signatures = + if !self.holder_sends_tx_signatures_first { self.holder_tx_signatures() } else { None }; let funding_tx_opt = self.signed_tx(); @@ -691,15 +701,32 @@ impl InteractiveTxSigningSession { self.holder_tx_signatures = Some(tx_signatures); let funding_tx_opt = self.signed_tx(); - let holder_tx_signatures = (self.has_received_commitment_signed - && (self.holder_sends_tx_signatures_first || self.has_received_tx_signatures())) - .then(|| { - self.holder_tx_signatures.clone().expect("Holder tx_signatures were just provided") - }); + let holder_tx_signatures = self.holder_tx_signatures(); Ok((holder_tx_signatures, funding_tx_opt)) } + pub fn provide_holder_shared_input_signature( + &mut self, shared_input_signature: Signature, + ) -> Result<(Option, Option), String> { + if self.shared_input().is_none() { + return Err("No shared input exists for this transaction".to_string()); + } + + let holder_tx_signatures = self.holder_tx_signatures.as_mut().ok_or_else(|| { + "Holder witnesses must be provided before the shared input signature".to_string() + })?; + if holder_tx_signatures.shared_input_signature.is_some() { + return Err("The shared input signature was already provided".to_string()); + } + + holder_tx_signatures.shared_input_signature = Some(shared_input_signature); + + let funding_tx_opt = self.signed_tx(); + let holder_tx_signatures = self.holder_tx_signatures(); + Ok((holder_tx_signatures, funding_tx_opt)) + } + pub fn remote_inputs_count(&self) -> usize { let shared_index = self.unsigned_tx.shared_input_index.as_ref(); self.unsigned_tx @@ -750,9 +777,9 @@ impl InteractiveTxSigningSession { /// Returns `Some` with the fully signed transaction if both holder and counterparty signatures /// are available. pub fn signed_tx(&self) -> Option { - let holder_tx_signatures = self.holder_tx_signatures.as_ref()?; - let counterparty_tx_signatures = self.counterparty_tx_signatures.as_ref()?; - let shared_input_signature = self.shared_input_signature.as_ref(); + let holder_tx_signatures = self.holder_tx_signatures()?; + let counterparty_tx_signatures = self.counterparty_tx_signatures.clone()?; + let shared_input_signature = self.shared_input_signature.clone(); self.unsigned_tx.finalize( holder_tx_signatures, counterparty_tx_signatures, diff --git a/lightning/src/sign/ecdsa.rs b/lightning/src/sign/ecdsa.rs index e13285722af..c0bd3759caa 100644 --- a/lightning/src/sign/ecdsa.rs +++ b/lightning/src/sign/ecdsa.rs @@ -254,8 +254,14 @@ pub trait EcdsaChannelSigner: ChannelSigner { /// /// `input_index`: The index of the input within the new funding transaction `tx`, /// spending the previous funding transaction's output + /// + /// An `Err` can be returned to signal that the signer is unavailable/cannot produce a valid + /// signature and should be retried later. Once the signer is ready to provide a signature after + /// previously returning an `Err`, [`ChannelManager::signer_unblocked`] must be called. + /// + /// [`ChannelManager::signer_unblocked`]: crate::ln::channelmanager::ChannelManager::signer_unblocked fn sign_splice_shared_input( &self, channel_parameters: &ChannelTransactionParameters, tx: &Transaction, input_index: usize, secp_ctx: &Secp256k1, - ) -> Signature; + ) -> Result; } diff --git a/lightning/src/sign/mod.rs b/lightning/src/sign/mod.rs index 3237149338b..5724ffdbcba 100644 --- a/lightning/src/sign/mod.rs +++ b/lightning/src/sign/mod.rs @@ -1928,7 +1928,7 @@ impl EcdsaChannelSigner for InMemorySigner { fn sign_splice_shared_input( &self, channel_parameters: &ChannelTransactionParameters, tx: &Transaction, input_index: usize, secp_ctx: &Secp256k1, - ) -> Signature { + ) -> Result { assert!(channel_parameters.is_populated(), "Channel parameters must be fully populated"); assert_eq!( tx.input[input_index].previous_output, @@ -1954,7 +1954,7 @@ impl EcdsaChannelSigner for InMemorySigner { ) .unwrap()[..]; let msg = hash_to_message!(sighash); - sign(secp_ctx, &msg, &funding_key) + Ok(sign(secp_ctx, &msg, &funding_key)) } } diff --git a/lightning/src/util/dyn_signer.rs b/lightning/src/util/dyn_signer.rs index 436eaabda34..5da284d25a4 100644 --- a/lightning/src/util/dyn_signer.rs +++ b/lightning/src/util/dyn_signer.rs @@ -90,7 +90,7 @@ delegate!(DynSigner, EcdsaChannelSigner, inner, fn sign_holder_htlc_transaction(, htlc_tx: &Transaction, input: usize, htlc_descriptor: &HTLCDescriptor, secp_ctx: &Secp256k1) -> Result, fn sign_splice_shared_input(, channel_parameters: &ChannelTransactionParameters, - tx: &Transaction, input_index: usize, secp_ctx: &Secp256k1) -> Signature + tx: &Transaction, input_index: usize, secp_ctx: &Secp256k1) -> Result ); delegate!(DynSigner, ChannelSigner, diff --git a/lightning/src/util/test_channel_signer.rs b/lightning/src/util/test_channel_signer.rs index 8435e7fa437..668bbebad05 100644 --- a/lightning/src/util/test_channel_signer.rs +++ b/lightning/src/util/test_channel_signer.rs @@ -103,6 +103,7 @@ pub enum SignerOp { SignClosingTransaction, SignHolderAnchorInput, SignChannelAnnouncementWithFundingKey, + SignSpliceSharedInput, } impl SignerOp { @@ -120,6 +121,7 @@ impl SignerOp { SignerOp::SignClosingTransaction, SignerOp::SignHolderAnchorInput, SignerOp::SignChannelAnnouncementWithFundingKey, + SignerOp::SignSpliceSharedInput, ] } } @@ -507,7 +509,11 @@ impl EcdsaChannelSigner for TestChannelSigner { fn sign_splice_shared_input( &self, channel_parameters: &ChannelTransactionParameters, tx: &Transaction, input_index: usize, secp_ctx: &Secp256k1, - ) -> Signature { + ) -> Result { + #[cfg(any(test, feature = "_test_utils"))] + if !self.is_signer_available(SignerOp::SignSpliceSharedInput) { + return Err(()); + } self.inner.sign_splice_shared_input(channel_parameters, tx, input_index, secp_ctx) } }