Skip to content

Commit

Permalink
Only promote V2 unfunded to Channel when receiving initial commitment…
Browse files Browse the repository at this point in the history
… signed
  • Loading branch information
dunxen committed Nov 25, 2024
1 parent 6790187 commit ac3fdbf
Show file tree
Hide file tree
Showing 3 changed files with 134 additions and 80 deletions.
58 changes: 35 additions & 23 deletions lightning/src/ln/channel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1801,15 +1801,19 @@ pub(super) trait InteractivelyFunded<SP: Deref> where SP::Target: SignerProvider
},
};

let funding_ready_for_sig_event = None;
if signing_session.local_inputs_count() == 0 {
let funding_ready_for_sig_event = if signing_session.local_inputs_count() == 0 {
debug_assert_eq!(our_funding_satoshis, 0);
if signing_session.provide_holder_witnesses(context.channel_id, Vec::new()).is_err() {
debug_assert!(
false,
"Zero inputs were provided & zero witnesses were provided, but a count mismatch was somehow found",
);
return Err(ChannelError::Close((
"V2 channel rejected due to sender error".into(),
ClosureReason::HolderForceClosed { broadcasted_latest_txn: Some(false) }
)));
}
None
} else {
// TODO(dual_funding): Send event for signing if we've contributed funds.
// Inform the user that SIGHASH_ALL must be used for all signatures when contributing
Expand All @@ -1825,7 +1829,15 @@ pub(super) trait InteractivelyFunded<SP: Deref> where SP::Target: SignerProvider
// will prevent the funding transaction from being relayed on the bitcoin network and hence being
// confirmed.
// </div>
}
debug_assert!(
false,
"We don't support users providing inputs but somehow we had more than zero inputs",
);
return Err(ChannelError::Close((
"V2 channel rejected due to sender error".into(),
ClosureReason::HolderForceClosed { broadcasted_latest_txn: Some(false) }
)));
};

context.channel_state = ChannelState::FundingNegotiated;

Expand Down Expand Up @@ -5693,7 +5705,7 @@ impl<SP: Deref> Channel<SP> where
}
}

pub fn tx_signatures<L: Deref>(&mut self, msg: &msgs::TxSignatures, logger: &L) -> Result<(Option<msgs::TxSignatures>, Option<Transaction>), ChannelError>
pub fn tx_signatures<L: Deref>(&mut self, msg: &msgs::TxSignatures, logger: &L) -> Result<Option<msgs::TxSignatures>, ChannelError>
where L::Target: Logger
{
if !matches!(self.context.channel_state, ChannelState::FundingNegotiated) {
Expand Down Expand Up @@ -5730,12 +5742,10 @@ impl<SP: Deref> Channel<SP> where
// for spending. Doesn't seem to be anything in rust-bitcoin.
}

let (tx_signatures_opt, funding_tx_opt) = signing_session.received_tx_signatures(msg.clone())
let (tx_signatures_opt, funding_tx) = signing_session.received_tx_signatures(msg.clone())
.map_err(|_| ChannelError::Warn("Witness count did not match contributed input count".to_string()))?;
if funding_tx_opt.is_some() {
self.context.channel_state = ChannelState::AwaitingChannelReady(AwaitingChannelReadyFlags::new());
}
self.context.funding_transaction = funding_tx_opt.clone();
self.context.channel_state = ChannelState::AwaitingChannelReady(AwaitingChannelReadyFlags::new());
self.context.funding_transaction = Some(funding_tx);

self.context.next_funding_txid = None;

Expand All @@ -5745,10 +5755,10 @@ impl<SP: Deref> Channel<SP> where
if tx_signatures_opt.is_some() && self.context.channel_state.is_monitor_update_in_progress() {
log_debug!(logger, "Not sending tx_signatures: a monitor update is in progress. Setting monitor_pending_tx_signatures.");
self.context.monitor_pending_tx_signatures = tx_signatures_opt;
return Ok((None, None));
return Ok(None);
}

Ok((tx_signatures_opt, funding_tx_opt))
Ok(tx_signatures_opt)
} else {
Err(ChannelError::Close((
"Unexpected tx_signatures. No funding transaction awaiting signatures".to_string(),
Expand Down Expand Up @@ -8726,6 +8736,8 @@ pub(super) struct OutboundV2Channel<SP: Deref> where SP::Target: SignerProvider
pub dual_funding_context: DualFundingChannelContext,
/// The current interactive transaction construction session under negotiation.
interactive_tx_constructor: Option<InteractiveTxConstructor>,
/// The signing session created after `tx_complete` handling
pub interactive_tx_signing_session: Option<InteractiveTxSigningSession>,
}

impl<SP: Deref> OutboundV2Channel<SP> where SP::Target: SignerProvider {
Expand Down Expand Up @@ -8785,6 +8797,7 @@ impl<SP: Deref> OutboundV2Channel<SP> where SP::Target: SignerProvider {
our_funding_inputs: funding_inputs,
},
interactive_tx_constructor: None,
interactive_tx_signing_session: None,
};
Ok(chan)
}
Expand Down Expand Up @@ -8852,13 +8865,11 @@ impl<SP: Deref> OutboundV2Channel<SP> where SP::Target: SignerProvider {
}
}

pub fn into_channel(self, signing_session: InteractiveTxSigningSession) -> Result<Channel<SP>, ChannelError>{
let channel = Channel {
pub fn into_channel(self) -> Channel<SP> {
Channel {
context: self.context,
interactive_tx_signing_session: Some(signing_session),
};

Ok(channel)
interactive_tx_signing_session: self.interactive_tx_signing_session,
}
}
}

Expand All @@ -8869,6 +8880,8 @@ pub(super) struct InboundV2Channel<SP: Deref> where SP::Target: SignerProvider {
pub dual_funding_context: DualFundingChannelContext,
/// The current interactive transaction construction session under negotiation.
interactive_tx_constructor: Option<InteractiveTxConstructor>,
/// The signing session created after `tx_complete` handling
pub interactive_tx_signing_session: Option<InteractiveTxSigningSession>,
}

impl<SP: Deref> InboundV2Channel<SP> where SP::Target: SignerProvider {
Expand Down Expand Up @@ -8970,6 +8983,7 @@ impl<SP: Deref> InboundV2Channel<SP> where SP::Target: SignerProvider {
context,
dual_funding_context,
interactive_tx_constructor,
interactive_tx_signing_session: None,
unfunded_context: UnfundedChannelContext { unfunded_channel_age_ticks: 0 },
})
}
Expand Down Expand Up @@ -9046,13 +9060,11 @@ impl<SP: Deref> InboundV2Channel<SP> where SP::Target: SignerProvider {
self.generate_accept_channel_v2_message()
}

pub fn into_channel(self, signing_session: InteractiveTxSigningSession) -> Result<Channel<SP>, ChannelError>{
let channel = Channel {
pub fn into_channel(self) -> Channel<SP> {
Channel {
context: self.context,
interactive_tx_signing_session: Some(signing_session),
};

Ok(channel)
interactive_tx_signing_session: self.interactive_tx_signing_session,
}
}
}

Expand Down
132 changes: 94 additions & 38 deletions lightning/src/ln/channelmanager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8305,7 +8305,7 @@ where
peer_state.pending_msg_events.push(msg_send_event);
};
if let Some(mut signing_session) = signing_session_opt {
let (commitment_signed, funding_ready_for_sig_event_opt) = match chan_phase_entry.get_mut() {
let (commitment_signed, funding_ready_for_sig_event_opt) = match channel_phase {
ChannelPhase::UnfundedOutboundV2(chan) => {
chan.funding_tx_constructed(&mut signing_session, &self.logger)
},
Expand All @@ -8316,18 +8316,17 @@ where
"Got a tx_complete message with no interactive transaction construction expected or in-progress"
.into())),
}.map_err(|err| MsgHandleErrInternal::send_err_msg_no_close(format!("{}", err), msg.channel_id))?;
let (channel_id, channel_phase) = chan_phase_entry.remove_entry();
let channel = match channel_phase {
ChannelPhase::UnfundedOutboundV2(chan) => chan.into_channel(signing_session),
ChannelPhase::UnfundedInboundV2(chan) => chan.into_channel(signing_session),
match channel_phase {
ChannelPhase::UnfundedOutboundV2(chan) => chan.interactive_tx_signing_session = Some(signing_session),
ChannelPhase::UnfundedInboundV2(chan) => chan.interactive_tx_signing_session = Some(signing_session),
_ => {
debug_assert!(false); // It cannot be another variant as we are in the `Ok` branch of the above match.
Err(ChannelError::Warn(
let err = ChannelError::Warn(
"Got a tx_complete message with no interactive transaction construction expected or in-progress"
.into()))
.into());
return Err(MsgHandleErrInternal::send_err_msg_no_close(format!("{}", err), msg.channel_id));
},
}.map_err(|err| MsgHandleErrInternal::send_err_msg_no_close(format!("{}", err), msg.channel_id))?;
peer_state.channel_by_id.insert(channel_id, ChannelPhase::Funded(channel));
}
if let Some(funding_ready_for_sig_event) = funding_ready_for_sig_event_opt {
let mut pending_events = self.pending_events.lock().unwrap();
pending_events.push_back((funding_ready_for_sig_event, None));
Expand Down Expand Up @@ -8370,14 +8369,14 @@ where
match channel_phase {
ChannelPhase::Funded(chan) => {
let logger = WithChannelContext::from(&self.logger, &chan.context, None);
let (tx_signatures_opt, funding_tx_opt) = try_chan_phase_entry!(self, peer_state, chan.tx_signatures(msg, &&logger), chan_phase_entry);
let tx_signatures_opt = try_chan_phase_entry!(self, peer_state, chan.tx_signatures(msg, &&logger), chan_phase_entry);
if let Some(tx_signatures) = tx_signatures_opt {
peer_state.pending_msg_events.push(events::MessageSendEvent::SendTxSignatures {
node_id: *counterparty_node_id,
msg: tx_signatures,
});
}
if let Some(ref funding_tx) = funding_tx_opt {
if let Some(ref funding_tx) = chan.context.unbroadcasted_funding() {
self.tx_broadcaster.broadcast_transactions(&[funding_tx]);
{
let mut pending_events = self.pending_events.lock().unwrap();
Expand Down Expand Up @@ -8840,46 +8839,103 @@ where
})?;
let mut peer_state_lock = peer_state_mutex.lock().unwrap();
let peer_state = &mut *peer_state_lock;
match peer_state.channel_by_id.entry(msg.channel_id) {
let (channel_id, mut chan) = match peer_state.channel_by_id.entry(msg.channel_id) {
hash_map::Entry::Occupied(mut chan_phase_entry) => {
if let ChannelPhase::Funded(chan) = chan_phase_entry.get_mut() {
let logger = WithChannelContext::from(&self.logger, &chan.context, None);
let funding_txo = chan.context.get_funding_txo();

if chan.interactive_tx_signing_session.is_some() {
let monitor = try_chan_phase_entry!(
self, peer_state, chan.commitment_signed_initial_v2(msg, best_block, &self.signer_provider, &&logger),
chan_phase_entry);
let monitor_res = self.chain_monitor.watch_channel(monitor.get_funding_txo().0, monitor);
if let Ok(persist_state) = monitor_res {
handle_new_monitor_update!(self, persist_state, peer_state_lock, peer_state,
per_peer_state, chan, INITIAL_MONITOR);
let channel_phase = chan_phase_entry.get_mut();
match channel_phase {
ChannelPhase::UnfundedOutboundV2(chan) => {
if chan.interactive_tx_signing_session.is_some() {
let (channel_id, mut channel_phase) = chan_phase_entry.remove_entry();
match channel_phase {
ChannelPhase::UnfundedOutboundV2(chan) => {
(channel_id, chan.into_channel())
}
_ => {
debug_assert!(false, "The channel phase was not UnfundedOutboundV2");
let err = ChannelError::close(
"Closing due to unexpected sender error".into());
return Err(convert_chan_phase_err!(self, peer_state, err, &mut channel_phase,
&channel_id).1)
}
}
} else {
let logger = WithChannelContext::from(&self.logger, &chan.context, None);
log_error!(logger, "Persisting initial ChannelMonitor failed, implying the funding outpoint was duplicated");
try_chan_phase_entry!(self, peer_state, Err(ChannelError::Close(
(
"Channel funding outpoint was a duplicate".to_owned(),
ClosureReason::HolderForceClosed { broadcasted_latest_txn: Some(false) },
)
)), chan_phase_entry)
return try_chan_phase_entry!(self, peer_state, Err(ChannelError::close(
"Got a commitment_signed message for a V2 channel before funding transaction constructed!".into())), chan_phase_entry);
}
} else {
},
ChannelPhase::UnfundedInboundV2(chan) => {
// TODO(dual_funding): This should be somewhat DRYable when #3418 is merged.
if chan.interactive_tx_signing_session.is_some() {
let (channel_id, mut channel_phase) = chan_phase_entry.remove_entry();
match channel_phase {
ChannelPhase::UnfundedInboundV2(chan) => {
(channel_id, chan.into_channel())
}
_ => {
debug_assert!(false, "The channel phase was not UnfundedInboundV2");
let err = ChannelError::close(
"Closing due to unexpected sender error".into());
return Err(convert_chan_phase_err!(self, peer_state, err, &mut channel_phase,
&channel_id).1)
}
}
} else {
return try_chan_phase_entry!(self, peer_state, Err(ChannelError::close(
"Got a commitment_signed message for a V2 channel before funding transaction constructed!".into())), chan_phase_entry);
}
},
ChannelPhase::Funded(chan) => {
let logger = WithChannelContext::from(&self.logger, &chan.context, None);
let funding_txo = chan.context.get_funding_txo();
let monitor_update_opt = try_chan_phase_entry!(
self, peer_state, chan.commitment_signed(msg, &&logger), chan_phase_entry);
if let Some(monitor_update) = monitor_update_opt {
handle_new_monitor_update!(self, funding_txo.unwrap(), monitor_update, peer_state_lock,
peer_state, per_peer_state, chan);
}
return Ok(())
},
_ => {
return try_chan_phase_entry!(self, peer_state, Err(ChannelError::close(
"Got a commitment_signed message for an unfunded channel!".into())), chan_phase_entry);
}
Ok(())
} else {
return try_chan_phase_entry!(self, peer_state, Err(ChannelError::close(
"Got a commitment_signed message for an unfunded channel!".into())), chan_phase_entry);
}
},
hash_map::Entry::Vacant(_) => Err(MsgHandleErrInternal::send_err_msg_no_close(format!("Got a message for a channel from the wrong node! No such channel for the passed counterparty_node_id {}", counterparty_node_id), msg.channel_id))
hash_map::Entry::Vacant(_) => return Err(MsgHandleErrInternal::send_err_msg_no_close(
format!("Got a message for a channel from the wrong node! No such channel for the passed counterparty_node_id {}",
counterparty_node_id), msg.channel_id))
};
let logger = WithChannelContext::from(&self.logger, &chan.context, None);
let monitor = match chan.commitment_signed_initial_v2(msg, best_block, &self.signer_provider, &&logger) {
Ok(monitor) => monitor,
Err(err) => return Err(convert_chan_phase_err!(self, peer_state, err, &mut ChannelPhase::Funded(chan), &channel_id).1),
};
let monitor_res = self.chain_monitor.watch_channel(monitor.get_funding_txo().0, monitor);
if let Ok(persist_state) = monitor_res {
let mut occupied_entry = peer_state.channel_by_id.entry(channel_id).insert(ChannelPhase::Funded(chan));
let channel_phase_entry = occupied_entry.get_mut();
match channel_phase_entry {
ChannelPhase::Funded(chan) => { handle_new_monitor_update!(self, persist_state, peer_state_lock, peer_state,
per_peer_state, chan, INITIAL_MONITOR); },
channel_phase => {
debug_assert!(false, "Expected a ChannelPhase::Funded");
let err = ChannelError::close(
"Closing due to unexpected sender error".into());
return Err(convert_chan_phase_err!(self, peer_state, err, channel_phase,
&channel_id).1)
},
}
} else {
log_error!(logger, "Persisting initial ChannelMonitor failed, implying the funding outpoint was duplicated");
let err = ChannelError::Close(
(
"Channel funding outpoint was a duplicate".to_owned(),
ClosureReason::HolderForceClosed { broadcasted_latest_txn: Some(false) },
)
);
return Err(convert_chan_phase_err!(self, peer_state, err, &mut ChannelPhase::Funded(chan), &channel_id).1);
}
Ok(())
}

fn push_decode_update_add_htlcs(&self, mut update_add_htlcs: (u64, Vec<msgs::UpdateAddHTLC>)) {
Expand Down
24 changes: 5 additions & 19 deletions lightning/src/ln/interactivetxs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -320,10 +320,7 @@ impl InteractiveTxSigningSession {
/// unsigned transaction.
pub fn received_tx_signatures(
&mut self, tx_signatures: TxSignatures,
) -> Result<(Option<TxSignatures>, Option<Transaction>), ()> {
if self.counterparty_sent_tx_signatures {
return Ok((None, None));
};
) -> Result<(Option<TxSignatures>, Transaction), ()> {
if self.remote_inputs_count() != tx_signatures.witnesses.len() {
return Err(());
}
Expand All @@ -336,13 +333,7 @@ impl InteractiveTxSigningSession {
None
};

let funding_tx = if self.holder_tx_signatures.is_some() {
Some(self.finalize_funding_tx())
} else {
None
};

Ok((holder_tx_signatures, funding_tx))
Ok((holder_tx_signatures, self.finalize_funding_tx()))
}

/// Provides the holder witnesses for the unsigned transaction.
Expand All @@ -351,7 +342,7 @@ impl InteractiveTxSigningSession {
/// unsigned transaction.
pub fn provide_holder_witnesses(
&mut self, channel_id: ChannelId, witnesses: Vec<Witness>,
) -> Result<Option<TxSignatures>, ()> {
) -> Result<(), ()> {
if self.local_inputs_count() != witnesses.len() {
return Err(());
}
Expand All @@ -363,13 +354,8 @@ impl InteractiveTxSigningSession {
witnesses: witnesses.into_iter().collect(),
shared_input_signature: None,
});
if self.received_commitment_signed
&& (self.holder_sends_tx_signatures_first || self.counterparty_sent_tx_signatures)
{
Ok(self.holder_tx_signatures.clone())
} else {
Ok(None)
}

Ok(())
}

pub fn remote_inputs_count(&self) -> usize {
Expand Down

0 comments on commit ac3fdbf

Please sign in to comment.