Skip to content

Commit 526bf62

Browse files
wpaulinojkczyz
andcommitted
Allow InteractiveTxConstructor use in splicing
InteractiveTxConstructor was only used in PendingV2Channel methods, but for splicing those methods are needed for FundedChannel, too. Refactor the code such that each type has a method for accessing its InteractiveTxConstructor such that it can be called in either use, refactoring code out of PendingV2Channel as needed. Co-authored-by: Wilmer Paulino <[email protected]> Co-authored-by: Jeffrey Czyz <[email protected]>
1 parent d915f6e commit 526bf62

File tree

3 files changed

+306
-267
lines changed

3 files changed

+306
-267
lines changed

lightning/src/ln/channel.rs

Lines changed: 179 additions & 173 deletions
Original file line numberDiff line numberDiff line change
@@ -58,9 +58,8 @@ use crate::ln::channelmanager::{
5858
#[cfg(splicing)]
5959
use crate::ln::interactivetxs::{calculate_change_output_value, AbortReason};
6060
use crate::ln::interactivetxs::{
61-
get_output_weight, HandleTxCompleteResult, InteractiveTxConstructor,
62-
InteractiveTxConstructorArgs, InteractiveTxMessageSendResult, InteractiveTxSigningSession,
63-
SharedOwnedInput, SharedOwnedOutput, TX_COMMON_FIELDS_WEIGHT,
61+
get_output_weight, InteractiveTxConstructor, InteractiveTxConstructorArgs,
62+
InteractiveTxSigningSession, SharedOwnedInput, SharedOwnedOutput, TX_COMMON_FIELDS_WEIGHT,
6463
};
6564
use crate::ln::msgs;
6665
use crate::ln::msgs::{ClosingSigned, ClosingSignedFeeRange, DecodeError, OnionErrorPacket};
@@ -1728,6 +1727,15 @@ where
17281727
}
17291728
}
17301729

1730+
pub fn interactive_tx_constructor_mut(&mut self) -> Option<&mut InteractiveTxConstructor> {
1731+
match &mut self.phase {
1732+
ChannelPhase::UnfundedV2(chan) => chan.interactive_tx_constructor.as_mut(),
1733+
#[cfg(splicing)]
1734+
ChannelPhase::Funded(chan) => chan.interactive_tx_constructor_mut(),
1735+
_ => None,
1736+
}
1737+
}
1738+
17311739
#[rustfmt::skip]
17321740
pub fn funding_signed<L: Deref>(
17331741
&mut self, msg: &msgs::FundingSigned, best_block: BestBlock, signer_provider: &SP, logger: &L
@@ -1761,16 +1769,71 @@ where
17611769
}
17621770

17631771
pub fn funding_tx_constructed<L: Deref>(
1764-
&mut self, signing_session: InteractiveTxSigningSession, logger: &L,
1772+
&mut self, logger: &L,
17651773
) -> Result<(msgs::CommitmentSigned, Option<Event>), ChannelError>
17661774
where
17671775
L::Target: Logger,
17681776
{
1769-
if let ChannelPhase::UnfundedV2(chan) = &mut self.phase {
1770-
let logger = WithChannelContext::from(logger, &chan.context, None);
1771-
chan.funding_tx_constructed(signing_session, &&logger)
1772-
} else {
1773-
Err(ChannelError::Warn("Got a tx_complete message with no interactive transaction construction expected or in-progress".to_owned()))
1777+
let logger = WithChannelContext::from(logger, self.context(), None);
1778+
match &mut self.phase {
1779+
ChannelPhase::UnfundedV2(chan) => {
1780+
let mut signing_session = chan
1781+
.interactive_tx_constructor
1782+
.take()
1783+
.expect("PendingV2Channel::interactive_tx_constructor should be set")
1784+
.into_signing_session();
1785+
let (commitment_signed, event) = chan.context.funding_tx_constructed(
1786+
&mut chan.funding,
1787+
&mut signing_session,
1788+
false,
1789+
chan.unfunded_context.transaction_number(),
1790+
&&logger,
1791+
)?;
1792+
1793+
chan.interactive_tx_signing_session = Some(signing_session);
1794+
1795+
return Ok((commitment_signed, event));
1796+
},
1797+
#[cfg(splicing)]
1798+
ChannelPhase::Funded(chan) => {
1799+
if let Some(pending_splice) = chan.pending_splice.as_mut() {
1800+
if let Some(funding_negotiation) = pending_splice.funding_negotiation.take() {
1801+
if let FundingNegotiation::Pending(
1802+
mut funding,
1803+
interactive_tx_constructor,
1804+
) = funding_negotiation
1805+
{
1806+
let mut signing_session =
1807+
interactive_tx_constructor.into_signing_session();
1808+
let (commitment_signed, event) = chan.context.funding_tx_constructed(
1809+
&mut funding,
1810+
&mut signing_session,
1811+
true,
1812+
chan.holder_commitment_point.transaction_number(),
1813+
&&logger,
1814+
)?;
1815+
1816+
chan.interactive_tx_signing_session = Some(signing_session);
1817+
pending_splice.funding_negotiation =
1818+
Some(FundingNegotiation::AwaitingSignatures(funding));
1819+
1820+
return Ok((commitment_signed, event));
1821+
} else {
1822+
// Replace the taken state
1823+
pending_splice.funding_negotiation = Some(funding_negotiation);
1824+
}
1825+
}
1826+
}
1827+
1828+
return Err(ChannelError::Warn(
1829+
"Got a transaction negotiation message in an invalid state".to_owned(),
1830+
));
1831+
},
1832+
_ => {
1833+
return Err(ChannelError::Warn(
1834+
"Got a transaction negotiation message in an invalid phase".to_owned(),
1835+
))
1836+
},
17741837
}
17751838
}
17761839

@@ -2771,170 +2834,6 @@ where
27712834
}
27722835
}
27732836

2774-
impl<SP: Deref> PendingV2Channel<SP>
2775-
where
2776-
SP::Target: SignerProvider,
2777-
{
2778-
pub fn tx_add_input(&mut self, msg: &msgs::TxAddInput) -> InteractiveTxMessageSendResult {
2779-
InteractiveTxMessageSendResult(match &mut self.interactive_tx_constructor {
2780-
Some(ref mut tx_constructor) => tx_constructor
2781-
.handle_tx_add_input(msg)
2782-
.map_err(|reason| reason.into_tx_abort_msg(self.context.channel_id())),
2783-
None => Err(msgs::TxAbort {
2784-
channel_id: self.context.channel_id(),
2785-
data: b"No interactive transaction negotiation in progress".to_vec(),
2786-
}),
2787-
})
2788-
}
2789-
2790-
pub fn tx_add_output(&mut self, msg: &msgs::TxAddOutput) -> InteractiveTxMessageSendResult {
2791-
InteractiveTxMessageSendResult(match &mut self.interactive_tx_constructor {
2792-
Some(ref mut tx_constructor) => tx_constructor
2793-
.handle_tx_add_output(msg)
2794-
.map_err(|reason| reason.into_tx_abort_msg(self.context.channel_id())),
2795-
None => Err(msgs::TxAbort {
2796-
channel_id: self.context.channel_id(),
2797-
data: b"No interactive transaction negotiation in progress".to_vec(),
2798-
}),
2799-
})
2800-
}
2801-
2802-
pub fn tx_remove_input(&mut self, msg: &msgs::TxRemoveInput) -> InteractiveTxMessageSendResult {
2803-
InteractiveTxMessageSendResult(match &mut self.interactive_tx_constructor {
2804-
Some(ref mut tx_constructor) => tx_constructor
2805-
.handle_tx_remove_input(msg)
2806-
.map_err(|reason| reason.into_tx_abort_msg(self.context.channel_id())),
2807-
None => Err(msgs::TxAbort {
2808-
channel_id: self.context.channel_id(),
2809-
data: b"No interactive transaction negotiation in progress".to_vec(),
2810-
}),
2811-
})
2812-
}
2813-
2814-
pub fn tx_remove_output(
2815-
&mut self, msg: &msgs::TxRemoveOutput,
2816-
) -> InteractiveTxMessageSendResult {
2817-
InteractiveTxMessageSendResult(match &mut self.interactive_tx_constructor {
2818-
Some(ref mut tx_constructor) => tx_constructor
2819-
.handle_tx_remove_output(msg)
2820-
.map_err(|reason| reason.into_tx_abort_msg(self.context.channel_id())),
2821-
None => Err(msgs::TxAbort {
2822-
channel_id: self.context.channel_id(),
2823-
data: b"No interactive transaction negotiation in progress".to_vec(),
2824-
}),
2825-
})
2826-
}
2827-
2828-
pub fn tx_complete(&mut self, msg: &msgs::TxComplete) -> HandleTxCompleteResult {
2829-
let tx_constructor = match &mut self.interactive_tx_constructor {
2830-
Some(ref mut tx_constructor) => tx_constructor,
2831-
None => {
2832-
let tx_abort = msgs::TxAbort {
2833-
channel_id: msg.channel_id,
2834-
data: b"No interactive transaction negotiation in progress".to_vec(),
2835-
};
2836-
return HandleTxCompleteResult(Err(tx_abort));
2837-
},
2838-
};
2839-
2840-
let tx_complete = match tx_constructor.handle_tx_complete(msg) {
2841-
Ok(tx_complete) => tx_complete,
2842-
Err(reason) => {
2843-
return HandleTxCompleteResult(Err(reason.into_tx_abort_msg(msg.channel_id)))
2844-
},
2845-
};
2846-
2847-
HandleTxCompleteResult(Ok(tx_complete))
2848-
}
2849-
2850-
#[rustfmt::skip]
2851-
pub fn funding_tx_constructed<L: Deref>(
2852-
&mut self, mut signing_session: InteractiveTxSigningSession, logger: &L
2853-
) -> Result<(msgs::CommitmentSigned, Option<Event>), ChannelError>
2854-
where
2855-
L::Target: Logger
2856-
{
2857-
let transaction_number = self.unfunded_context.transaction_number();
2858-
2859-
let mut output_index = None;
2860-
let expected_spk = self.funding.get_funding_redeemscript().to_p2wsh();
2861-
for (idx, outp) in signing_session.unsigned_tx().outputs().enumerate() {
2862-
if outp.script_pubkey() == &expected_spk && outp.value() == self.funding.get_value_satoshis() {
2863-
if output_index.is_some() {
2864-
let msg = "Multiple outputs matched the expected script and value";
2865-
let reason = ClosureReason::ProcessingError { err: msg.to_owned() };
2866-
return Err(ChannelError::Close((msg.to_owned(), reason)));
2867-
}
2868-
output_index = Some(idx as u16);
2869-
}
2870-
}
2871-
let outpoint = if let Some(output_index) = output_index {
2872-
OutPoint { txid: signing_session.unsigned_tx().compute_txid(), index: output_index }
2873-
} else {
2874-
let msg = "No output matched the funding script_pubkey";
2875-
let reason = ClosureReason::ProcessingError { err: msg.to_owned() };
2876-
return Err(ChannelError::Close((msg.to_owned(), reason)));
2877-
};
2878-
self.funding.channel_transaction_parameters.funding_outpoint = Some(outpoint);
2879-
2880-
self.context.assert_no_commitment_advancement(transaction_number, "initial commitment_signed");
2881-
let commitment_signed = self.context.get_initial_commitment_signed(&self.funding, logger);
2882-
let commitment_signed = match commitment_signed {
2883-
Ok(commitment_signed) => commitment_signed,
2884-
Err(e) => {
2885-
self.funding.channel_transaction_parameters.funding_outpoint = None;
2886-
return Err(e)
2887-
},
2888-
};
2889-
2890-
let funding_ready_for_sig_event = if signing_session.local_inputs_count() == 0 {
2891-
debug_assert_eq!(self.funding_negotiation_context.our_funding_contribution_satoshis, 0);
2892-
if signing_session.provide_holder_witnesses(self.context.channel_id, Vec::new()).is_err() {
2893-
debug_assert!(
2894-
false,
2895-
"Zero inputs were provided & zero witnesses were provided, but a count mismatch was somehow found",
2896-
);
2897-
let msg = "V2 channel rejected due to sender error";
2898-
let reason = ClosureReason::ProcessingError { err: msg.to_owned() };
2899-
return Err(ChannelError::Close((msg.to_owned(), reason)));
2900-
}
2901-
None
2902-
} else {
2903-
// TODO(dual_funding): Send event for signing if we've contributed funds.
2904-
// Inform the user that SIGHASH_ALL must be used for all signatures when contributing
2905-
// inputs/signatures.
2906-
// Also warn the user that we don't do anything to prevent the counterparty from
2907-
// providing non-standard witnesses which will prevent the funding transaction from
2908-
// confirming. This warning must appear in doc comments wherever the user is contributing
2909-
// funds, whether they are initiator or acceptor.
2910-
//
2911-
// The following warning can be used when the APIs allowing contributing inputs become available:
2912-
// <div class="warning">
2913-
// WARNING: LDK makes no attempt to prevent the counterparty from using non-standard inputs which
2914-
// will prevent the funding transaction from being relayed on the bitcoin network and hence being
2915-
// confirmed.
2916-
// </div>
2917-
debug_assert!(
2918-
false,
2919-
"We don't support users providing inputs but somehow we had more than zero inputs",
2920-
);
2921-
let msg = "V2 channel rejected due to sender error";
2922-
let reason = ClosureReason::ProcessingError { err: msg.to_owned() };
2923-
return Err(ChannelError::Close((msg.to_owned(), reason)));
2924-
};
2925-
2926-
let mut channel_state = ChannelState::FundingNegotiated(FundingNegotiatedFlags::new());
2927-
channel_state.set_interactive_signing();
2928-
self.context.channel_state = channel_state;
2929-
2930-
// Clear the interactive transaction constructor
2931-
self.interactive_tx_constructor.take();
2932-
self.interactive_tx_signing_session = Some(signing_session);
2933-
2934-
Ok((commitment_signed, funding_ready_for_sig_event))
2935-
}
2936-
}
2937-
29382837
impl<SP: Deref> ChannelContext<SP>
29392838
where
29402839
SP::Target: SignerProvider,
@@ -5418,6 +5317,97 @@ where
54185317
Ok(())
54195318
}
54205319

5320+
#[rustfmt::skip]
5321+
fn funding_tx_constructed<L: Deref>(
5322+
&mut self, funding: &mut FundingScope, signing_session: &mut InteractiveTxSigningSession,
5323+
is_splice: bool, holder_commitment_transaction_number: u64, logger: &L
5324+
) -> Result<(msgs::CommitmentSigned, Option<Event>), ChannelError>
5325+
where
5326+
L::Target: Logger
5327+
{
5328+
let mut output_index = None;
5329+
let expected_spk = funding.get_funding_redeemscript().to_p2wsh();
5330+
for (idx, outp) in signing_session.unsigned_tx().outputs().enumerate() {
5331+
if outp.script_pubkey() == &expected_spk && outp.value() == funding.get_value_satoshis() {
5332+
if output_index.is_some() {
5333+
let msg = "Multiple outputs matched the expected script and value";
5334+
let reason = ClosureReason::ProcessingError { err: msg.to_owned() };
5335+
return Err(ChannelError::Close((msg.to_owned(), reason)));
5336+
}
5337+
output_index = Some(idx as u16);
5338+
}
5339+
}
5340+
let outpoint = if let Some(output_index) = output_index {
5341+
OutPoint { txid: signing_session.unsigned_tx().compute_txid(), index: output_index }
5342+
} else {
5343+
let msg = "No output matched the funding script_pubkey";
5344+
let reason = ClosureReason::ProcessingError { err: msg.to_owned() };
5345+
return Err(ChannelError::Close((msg.to_owned(), reason)));
5346+
};
5347+
funding
5348+
.channel_transaction_parameters.funding_outpoint = Some(outpoint);
5349+
5350+
if is_splice {
5351+
let message = "TODO Forced error, incomplete implementation".to_owned();
5352+
// TODO(splicing) Forced error, as the use case is not complete
5353+
return Err(ChannelError::Close((
5354+
message.clone(),
5355+
ClosureReason::HolderForceClosed { broadcasted_latest_txn: Some(false), message }
5356+
)));
5357+
}
5358+
5359+
self.assert_no_commitment_advancement(holder_commitment_transaction_number, "initial commitment_signed");
5360+
let commitment_signed = self.get_initial_commitment_signed(&funding, logger);
5361+
let commitment_signed = match commitment_signed {
5362+
Ok(commitment_signed) => commitment_signed,
5363+
Err(e) => {
5364+
funding.channel_transaction_parameters.funding_outpoint = None;
5365+
return Err(e)
5366+
},
5367+
};
5368+
5369+
let funding_ready_for_sig_event = if signing_session.local_inputs_count() == 0 {
5370+
if signing_session.provide_holder_witnesses(self.channel_id, Vec::new()).is_err() {
5371+
debug_assert!(
5372+
false,
5373+
"Zero inputs were provided & zero witnesses were provided, but a count mismatch was somehow found",
5374+
);
5375+
let msg = "V2 channel rejected due to sender error";
5376+
let reason = ClosureReason::ProcessingError { err: msg.to_owned() };
5377+
return Err(ChannelError::Close((msg.to_owned(), reason)));
5378+
}
5379+
None
5380+
} else {
5381+
// TODO(dual_funding): Send event for signing if we've contributed funds.
5382+
// Inform the user that SIGHASH_ALL must be used for all signatures when contributing
5383+
// inputs/signatures.
5384+
// Also warn the user that we don't do anything to prevent the counterparty from
5385+
// providing non-standard witnesses which will prevent the funding transaction from
5386+
// confirming. This warning must appear in doc comments wherever the user is contributing
5387+
// funds, whether they are initiator or acceptor.
5388+
//
5389+
// The following warning can be used when the APIs allowing contributing inputs become available:
5390+
// <div class="warning">
5391+
// WARNING: LDK makes no attempt to prevent the counterparty from using non-standard inputs which
5392+
// will prevent the funding transaction from being relayed on the bitcoin network and hence being
5393+
// confirmed.
5394+
// </div>
5395+
debug_assert!(
5396+
false,
5397+
"We don't support users providing inputs but somehow we had more than zero inputs",
5398+
);
5399+
let msg = "V2 channel rejected due to sender error";
5400+
let reason = ClosureReason::ProcessingError { err: msg.to_owned() };
5401+
return Err(ChannelError::Close((msg.to_owned(), reason)));
5402+
};
5403+
5404+
let mut channel_state = ChannelState::FundingNegotiated(FundingNegotiatedFlags::new());
5405+
channel_state.set_interactive_signing();
5406+
self.channel_state = channel_state;
5407+
5408+
Ok((commitment_signed, funding_ready_for_sig_event))
5409+
}
5410+
54215411
/// Asserts that the commitment tx numbers have not advanced from their initial number.
54225412
#[rustfmt::skip]
54235413
fn assert_no_commitment_advancement(&self, holder_commitment_transaction_number: u64, msg_name: &str) {
@@ -6033,6 +6023,22 @@ where
60336023
self.context.force_shutdown(&self.funding, closure_reason)
60346024
}
60356025

6026+
#[cfg(splicing)]
6027+
fn interactive_tx_constructor_mut(&mut self) -> Option<&mut InteractiveTxConstructor> {
6028+
self.pending_splice
6029+
.as_mut()
6030+
.and_then(|pending_splice| pending_splice.funding_negotiation.as_mut())
6031+
.and_then(|funding_negotiation| {
6032+
if let FundingNegotiation::Pending(_, interactive_tx_constructor) =
6033+
funding_negotiation
6034+
{
6035+
Some(interactive_tx_constructor)
6036+
} else {
6037+
None
6038+
}
6039+
})
6040+
}
6041+
60366042
#[rustfmt::skip]
60376043
fn check_remote_fee<F: Deref, L: Deref>(
60386044
channel_type: &ChannelTypeFeatures, fee_estimator: &LowerBoundedFeeEstimator<F>,

0 commit comments

Comments
 (0)