Skip to content

Commit 3e8b060

Browse files
committed
Free holding cell upon tx_signatures exchange
After cad88af, a few code paths that also lead to a quiescence exit were not accounted for. This commit addresses the last remaining path where we exit quiescence when we exchange `tx_signatures` with the counterparty.
1 parent 2665465 commit 3e8b060

File tree

2 files changed

+188
-82
lines changed

2 files changed

+188
-82
lines changed

lightning/src/ln/channelmanager.rs

Lines changed: 98 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -11543,90 +11543,106 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
1154311543
fn internal_tx_signatures(
1154411544
&self, counterparty_node_id: &PublicKey, msg: &msgs::TxSignatures,
1154511545
) -> Result<(), MsgHandleErrInternal> {
11546-
let per_peer_state = self.per_peer_state.read().unwrap();
11547-
let peer_state_mutex = per_peer_state.get(counterparty_node_id).ok_or_else(|| {
11548-
debug_assert!(false);
11549-
MsgHandleErrInternal::no_such_peer(counterparty_node_id, msg.channel_id)
11550-
})?;
11551-
let mut peer_state_lock = peer_state_mutex.lock().unwrap();
11552-
let peer_state = &mut *peer_state_lock;
11553-
match peer_state.channel_by_id.entry(msg.channel_id) {
11554-
hash_map::Entry::Occupied(mut chan_entry) => {
11555-
match chan_entry.get_mut().as_funded_mut() {
11556-
Some(chan) => {
11557-
let best_block_height = self.best_block.read().unwrap().height;
11558-
let FundingTxSigned {
11559-
commitment_signed,
11560-
counterparty_initial_commitment_signed_result,
11561-
tx_signatures,
11562-
funding_tx,
11563-
splice_negotiated,
11564-
splice_locked,
11565-
} = try_channel_entry!(
11566-
self,
11567-
peer_state,
11568-
chan.tx_signatures(msg, best_block_height, &self.logger),
11569-
chan_entry
11570-
);
11571-
11572-
// We should never be sending a `commitment_signed` in response to their
11573-
// `tx_signatures`.
11574-
debug_assert!(commitment_signed.is_none());
11575-
debug_assert!(counterparty_initial_commitment_signed_result.is_none());
11576-
11577-
if let Some(tx_signatures) = tx_signatures {
11578-
peer_state.pending_msg_events.push(
11579-
MessageSendEvent::SendTxSignatures {
11580-
node_id: *counterparty_node_id,
11581-
msg: tx_signatures,
11582-
},
11583-
);
11584-
}
11585-
if let Some(splice_locked) = splice_locked {
11586-
peer_state.pending_msg_events.push(
11587-
MessageSendEvent::SendSpliceLocked {
11588-
node_id: *counterparty_node_id,
11589-
msg: splice_locked,
11590-
},
11591-
);
11592-
}
11593-
if let Some((ref funding_tx, ref tx_type)) = funding_tx {
11594-
self.broadcast_interactive_funding(
11595-
chan,
11546+
let (result, holding_cell_res) = {
11547+
let per_peer_state = self.per_peer_state.read().unwrap();
11548+
let peer_state_mutex = per_peer_state.get(counterparty_node_id).ok_or_else(|| {
11549+
debug_assert!(false);
11550+
MsgHandleErrInternal::no_such_peer(counterparty_node_id, msg.channel_id)
11551+
})?;
11552+
let mut peer_state_lock = peer_state_mutex.lock().unwrap();
11553+
let peer_state = &mut *peer_state_lock;
11554+
match peer_state.channel_by_id.entry(msg.channel_id) {
11555+
hash_map::Entry::Occupied(mut chan_entry) => {
11556+
match chan_entry.get_mut().as_funded_mut() {
11557+
Some(chan) => {
11558+
let best_block_height = self.best_block.read().unwrap().height;
11559+
let FundingTxSigned {
11560+
commitment_signed,
11561+
counterparty_initial_commitment_signed_result,
11562+
tx_signatures,
1159611563
funding_tx,
11597-
Some(tx_type.clone()),
11598-
&self.logger,
11564+
splice_negotiated,
11565+
splice_locked,
11566+
} = try_channel_entry!(
11567+
self,
11568+
peer_state,
11569+
chan.tx_signatures(msg, best_block_height, &self.logger),
11570+
chan_entry
1159911571
);
11600-
}
11601-
if let Some(splice_negotiated) = splice_negotiated {
11602-
self.pending_events.lock().unwrap().push_back((
11603-
events::Event::SplicePending {
11604-
channel_id: msg.channel_id,
11605-
counterparty_node_id: *counterparty_node_id,
11606-
user_channel_id: chan.context.get_user_id(),
11607-
new_funding_txo: splice_negotiated.funding_txo,
11608-
channel_type: splice_negotiated.channel_type,
11609-
new_funding_redeem_script: splice_negotiated
11610-
.funding_redeem_script,
11611-
},
11612-
None,
11613-
));
11614-
}
11615-
},
11616-
None => {
11617-
let msg = "Got an unexpected tx_signatures message";
11618-
let reason = ClosureReason::ProcessingError { err: msg.to_owned() };
11619-
let err = ChannelError::Close((msg.to_owned(), reason));
11620-
try_channel_entry!(self, peer_state, Err(err), chan_entry)
11621-
},
11622-
}
11623-
Ok(())
11624-
},
11625-
hash_map::Entry::Vacant(_) => Err(MsgHandleErrInternal::no_such_channel_for_peer(
11626-
counterparty_node_id,
11627-
msg.channel_id,
11628-
)),
11629-
}
11572+
11573+
// We should never be sending a `commitment_signed` in response to their
11574+
// `tx_signatures`.
11575+
debug_assert!(commitment_signed.is_none());
11576+
debug_assert!(counterparty_initial_commitment_signed_result.is_none());
11577+
11578+
if let Some(tx_signatures) = tx_signatures {
11579+
peer_state.pending_msg_events.push(
11580+
MessageSendEvent::SendTxSignatures {
11581+
node_id: *counterparty_node_id,
11582+
msg: tx_signatures,
11583+
},
11584+
);
11585+
}
11586+
if let Some(splice_locked) = splice_locked {
11587+
peer_state.pending_msg_events.push(
11588+
MessageSendEvent::SendSpliceLocked {
11589+
node_id: *counterparty_node_id,
11590+
msg: splice_locked,
11591+
},
11592+
);
11593+
}
11594+
if let Some((ref funding_tx, ref tx_type)) = funding_tx {
11595+
self.broadcast_interactive_funding(
11596+
chan,
11597+
funding_tx,
11598+
Some(tx_type.clone()),
11599+
&self.logger,
11600+
);
11601+
}
11602+
// We consider a splice negotiated when we exchange `tx_signatures`,
11603+
// which also terminates quiescence.
11604+
let exited_quiescence = splice_negotiated.is_some();
11605+
if let Some(splice_negotiated) = splice_negotiated {
11606+
self.pending_events.lock().unwrap().push_back((
11607+
events::Event::SplicePending {
11608+
channel_id: msg.channel_id,
11609+
counterparty_node_id: *counterparty_node_id,
11610+
user_channel_id: chan.context.get_user_id(),
11611+
new_funding_txo: splice_negotiated.funding_txo,
11612+
channel_type: splice_negotiated.channel_type,
11613+
new_funding_redeem_script: splice_negotiated
11614+
.funding_redeem_script,
11615+
},
11616+
None,
11617+
));
11618+
}
11619+
let holding_cell_res = if exited_quiescence {
11620+
self.check_free_peer_holding_cells(peer_state)
11621+
} else {
11622+
Vec::new()
11623+
};
11624+
(Ok(()), holding_cell_res)
11625+
},
11626+
None => {
11627+
let msg = "Got an unexpected tx_signatures message";
11628+
let reason = ClosureReason::ProcessingError { err: msg.to_owned() };
11629+
let err = ChannelError::Close((msg.to_owned(), reason));
11630+
try_channel_entry!(self, peer_state, Err(err), chan_entry)
11631+
},
11632+
}
11633+
},
11634+
hash_map::Entry::Vacant(_) => (
11635+
Err(MsgHandleErrInternal::no_such_channel_for_peer(
11636+
counterparty_node_id,
11637+
msg.channel_id,
11638+
)),
11639+
Vec::new(),
11640+
),
11641+
}
11642+
};
11643+
11644+
self.handle_holding_cell_free_result(holding_cell_res);
11645+
result
1163011646
}
1163111647

1163211648
fn internal_tx_abort(

lightning/src/ln/splicing_tests.rs

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2165,6 +2165,96 @@ fn fail_splice_on_tx_complete_error() {
21652165
do_commitment_signed_dance(initiator, acceptor, &update.commitment_signed, false, false);
21662166
}
21672167

2168+
#[test]
2169+
fn free_holding_cell_on_tx_signatures_quiescence_exit() {
2170+
// Test that if there's an update in the holding cell while we're quiescent, that it gets freed
2171+
// upon exiting quiescence via the `tx_signatures` exchange.
2172+
let chanmon_cfgs = create_chanmon_cfgs(2);
2173+
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
2174+
let config = test_default_channel_config();
2175+
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[Some(config.clone()), Some(config)]);
2176+
let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
2177+
2178+
let initiator = &nodes[0];
2179+
let acceptor = &nodes[1];
2180+
let node_id_initiator = initiator.node.get_our_node_id();
2181+
let node_id_acceptor = acceptor.node.get_our_node_id();
2182+
2183+
let (_, _, channel_id, _) =
2184+
create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 100_000, 0);
2185+
2186+
let outputs = vec![TxOut {
2187+
value: Amount::from_sat(1_000),
2188+
script_pubkey: initiator.wallet_source.get_change_script().unwrap(),
2189+
}];
2190+
let contribution = initiate_splice_out(initiator, acceptor, channel_id, outputs);
2191+
negotiate_splice_tx(initiator, acceptor, channel_id, contribution);
2192+
2193+
// Queue an outgoing HTLC to the holding cell. It should be freed once we exit quiescence.
2194+
let (route, payment_hash, _payment_preimage, payment_secret) =
2195+
get_route_and_payment_hash!(initiator, acceptor, 1_000_000);
2196+
let onion = RecipientOnionFields::secret_only(payment_secret);
2197+
let payment_id = PaymentId(payment_hash.0);
2198+
initiator.node.send_payment_with_route(route, payment_hash, onion, payment_id).unwrap();
2199+
assert!(initiator.node.get_and_clear_pending_msg_events().is_empty());
2200+
2201+
let event = get_event!(initiator, Event::FundingTransactionReadyForSigning);
2202+
if let Event::FundingTransactionReadyForSigning {
2203+
channel_id,
2204+
counterparty_node_id,
2205+
unsigned_transaction,
2206+
..
2207+
} = event
2208+
{
2209+
let partially_signed_tx = initiator.wallet_source.sign_tx(unsigned_transaction).unwrap();
2210+
initiator
2211+
.node
2212+
.funding_transaction_signed(&channel_id, &counterparty_node_id, partially_signed_tx)
2213+
.unwrap();
2214+
} else {
2215+
unreachable!();
2216+
}
2217+
2218+
let update = get_htlc_update_msgs(initiator, &node_id_acceptor);
2219+
acceptor.node.handle_commitment_signed(node_id_initiator, &update.commitment_signed[0]);
2220+
check_added_monitors(&acceptor, 1);
2221+
2222+
let msg_events = acceptor.node.get_and_clear_pending_msg_events();
2223+
assert_eq!(msg_events.len(), 2, "{msg_events:?}");
2224+
if let MessageSendEvent::UpdateHTLCs { ref updates, .. } = &msg_events[0] {
2225+
let commitment_signed = &updates.commitment_signed[0];
2226+
initiator.node.handle_commitment_signed(node_id_acceptor, commitment_signed);
2227+
check_added_monitors(&initiator, 1);
2228+
} else {
2229+
panic!("Unexpected event {:?}", &msg_events[0]);
2230+
}
2231+
if let MessageSendEvent::SendTxSignatures { ref msg, .. } = &msg_events[1] {
2232+
initiator.node.handle_tx_signatures(node_id_acceptor, msg);
2233+
} else {
2234+
panic!("Unexpected event {:?}", &msg_events[1]);
2235+
}
2236+
2237+
// With `tx_signatures` exchanged, we've exited quiescence and should now see the outgoing HTLC
2238+
// update be sent.
2239+
let msg_events = initiator.node.get_and_clear_pending_msg_events();
2240+
assert_eq!(msg_events.len(), 2, "{msg_events:?}");
2241+
check_added_monitors(initiator, 1); // Outgoing HTLC monitor update
2242+
if let MessageSendEvent::SendTxSignatures { ref msg, .. } = &msg_events[0] {
2243+
acceptor.node.handle_tx_signatures(node_id_initiator, msg);
2244+
} else {
2245+
panic!("Unexpected event {:?}", &msg_events[0]);
2246+
}
2247+
if let MessageSendEvent::UpdateHTLCs { updates, .. } = &msg_events[1] {
2248+
acceptor.node.handle_update_add_htlc(node_id_initiator, &updates.update_add_htlcs[0]);
2249+
do_commitment_signed_dance(acceptor, initiator, &updates.commitment_signed, false, false);
2250+
} else {
2251+
panic!("Unexpected event {:?}", &msg_events[1]);
2252+
}
2253+
2254+
expect_splice_pending_event(initiator, &node_id_acceptor);
2255+
expect_splice_pending_event(acceptor, &node_id_initiator);
2256+
}
2257+
21682258
#[test]
21692259
fn fail_splice_on_channel_close() {
21702260
let chanmon_cfgs = create_chanmon_cfgs(2);

0 commit comments

Comments
 (0)