Skip to content

Commit b87e63f

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 5f6e99d commit b87e63f

File tree

2 files changed

+187
-82
lines changed

2 files changed

+187
-82
lines changed

lightning/src/ln/channelmanager.rs

Lines changed: 98 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -11457,90 +11457,106 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
1145711457
fn internal_tx_signatures(
1145811458
&self, counterparty_node_id: &PublicKey, msg: &msgs::TxSignatures,
1145911459
) -> Result<(), MsgHandleErrInternal> {
11460-
let per_peer_state = self.per_peer_state.read().unwrap();
11461-
let peer_state_mutex = per_peer_state.get(counterparty_node_id).ok_or_else(|| {
11462-
debug_assert!(false);
11463-
MsgHandleErrInternal::no_such_peer(counterparty_node_id, msg.channel_id)
11464-
})?;
11465-
let mut peer_state_lock = peer_state_mutex.lock().unwrap();
11466-
let peer_state = &mut *peer_state_lock;
11467-
match peer_state.channel_by_id.entry(msg.channel_id) {
11468-
hash_map::Entry::Occupied(mut chan_entry) => {
11469-
match chan_entry.get_mut().as_funded_mut() {
11470-
Some(chan) => {
11471-
let best_block_height = self.best_block.read().unwrap().height;
11472-
let FundingTxSigned {
11473-
commitment_signed,
11474-
counterparty_initial_commitment_signed_result,
11475-
tx_signatures,
11476-
funding_tx,
11477-
splice_negotiated,
11478-
splice_locked,
11479-
} = try_channel_entry!(
11480-
self,
11481-
peer_state,
11482-
chan.tx_signatures(msg, best_block_height, &self.logger),
11483-
chan_entry
11484-
);
11485-
11486-
// We should never be sending a `commitment_signed` in response to their
11487-
// `tx_signatures`.
11488-
debug_assert!(commitment_signed.is_none());
11489-
debug_assert!(counterparty_initial_commitment_signed_result.is_none());
11490-
11491-
if let Some(tx_signatures) = tx_signatures {
11492-
peer_state.pending_msg_events.push(
11493-
MessageSendEvent::SendTxSignatures {
11494-
node_id: *counterparty_node_id,
11495-
msg: tx_signatures,
11496-
},
11497-
);
11498-
}
11499-
if let Some(splice_locked) = splice_locked {
11500-
peer_state.pending_msg_events.push(
11501-
MessageSendEvent::SendSpliceLocked {
11502-
node_id: *counterparty_node_id,
11503-
msg: splice_locked,
11504-
},
11505-
);
11506-
}
11507-
if let Some((ref funding_tx, ref tx_type)) = funding_tx {
11508-
self.broadcast_interactive_funding(
11509-
chan,
11460+
let (result, holding_cell_res) = {
11461+
let per_peer_state = self.per_peer_state.read().unwrap();
11462+
let peer_state_mutex = per_peer_state.get(counterparty_node_id).ok_or_else(|| {
11463+
debug_assert!(false);
11464+
MsgHandleErrInternal::no_such_peer(counterparty_node_id, msg.channel_id)
11465+
})?;
11466+
let mut peer_state_lock = peer_state_mutex.lock().unwrap();
11467+
let peer_state = &mut *peer_state_lock;
11468+
match peer_state.channel_by_id.entry(msg.channel_id) {
11469+
hash_map::Entry::Occupied(mut chan_entry) => {
11470+
match chan_entry.get_mut().as_funded_mut() {
11471+
Some(chan) => {
11472+
let best_block_height = self.best_block.read().unwrap().height;
11473+
let FundingTxSigned {
11474+
commitment_signed,
11475+
counterparty_initial_commitment_signed_result,
11476+
tx_signatures,
1151011477
funding_tx,
11511-
Some(tx_type.clone()),
11512-
&self.logger,
11478+
splice_negotiated,
11479+
splice_locked,
11480+
} = try_channel_entry!(
11481+
self,
11482+
peer_state,
11483+
chan.tx_signatures(msg, best_block_height, &self.logger),
11484+
chan_entry
1151311485
);
11514-
}
11515-
if let Some(splice_negotiated) = splice_negotiated {
11516-
self.pending_events.lock().unwrap().push_back((
11517-
events::Event::SplicePending {
11518-
channel_id: msg.channel_id,
11519-
counterparty_node_id: *counterparty_node_id,
11520-
user_channel_id: chan.context.get_user_id(),
11521-
new_funding_txo: splice_negotiated.funding_txo,
11522-
channel_type: splice_negotiated.channel_type,
11523-
new_funding_redeem_script: splice_negotiated
11524-
.funding_redeem_script,
11525-
},
11526-
None,
11527-
));
11528-
}
11529-
},
11530-
None => {
11531-
let msg = "Got an unexpected tx_signatures message";
11532-
let reason = ClosureReason::ProcessingError { err: msg.to_owned() };
11533-
let err = ChannelError::Close((msg.to_owned(), reason));
11534-
try_channel_entry!(self, peer_state, Err(err), chan_entry)
11535-
},
11536-
}
11537-
Ok(())
11538-
},
11539-
hash_map::Entry::Vacant(_) => Err(MsgHandleErrInternal::no_such_channel_for_peer(
11540-
counterparty_node_id,
11541-
msg.channel_id,
11542-
)),
11543-
}
11486+
11487+
// We should never be sending a `commitment_signed` in response to their
11488+
// `tx_signatures`.
11489+
debug_assert!(commitment_signed.is_none());
11490+
debug_assert!(counterparty_initial_commitment_signed_result.is_none());
11491+
11492+
if let Some(tx_signatures) = tx_signatures {
11493+
peer_state.pending_msg_events.push(
11494+
MessageSendEvent::SendTxSignatures {
11495+
node_id: *counterparty_node_id,
11496+
msg: tx_signatures,
11497+
},
11498+
);
11499+
}
11500+
if let Some(splice_locked) = splice_locked {
11501+
peer_state.pending_msg_events.push(
11502+
MessageSendEvent::SendSpliceLocked {
11503+
node_id: *counterparty_node_id,
11504+
msg: splice_locked,
11505+
},
11506+
);
11507+
}
11508+
if let Some((ref funding_tx, ref tx_type)) = funding_tx {
11509+
self.broadcast_interactive_funding(
11510+
chan,
11511+
funding_tx,
11512+
Some(tx_type.clone()),
11513+
&self.logger,
11514+
);
11515+
}
11516+
// We consider a splice negotiated when we exchange `tx_signatures`,
11517+
// which also terminates quiescence.
11518+
let exited_quiescence = splice_negotiated.is_some();
11519+
if let Some(splice_negotiated) = splice_negotiated {
11520+
self.pending_events.lock().unwrap().push_back((
11521+
events::Event::SplicePending {
11522+
channel_id: msg.channel_id,
11523+
counterparty_node_id: *counterparty_node_id,
11524+
user_channel_id: chan.context.get_user_id(),
11525+
new_funding_txo: splice_negotiated.funding_txo,
11526+
channel_type: splice_negotiated.channel_type,
11527+
new_funding_redeem_script: splice_negotiated
11528+
.funding_redeem_script,
11529+
},
11530+
None,
11531+
));
11532+
}
11533+
let holding_cell_res = if exited_quiescence {
11534+
self.check_free_peer_holding_cells(peer_state)
11535+
} else {
11536+
Vec::new()
11537+
};
11538+
(Ok(()), holding_cell_res)
11539+
},
11540+
None => {
11541+
let msg = "Got an unexpected tx_signatures message";
11542+
let reason = ClosureReason::ProcessingError { err: msg.to_owned() };
11543+
let err = ChannelError::Close((msg.to_owned(), reason));
11544+
try_channel_entry!(self, peer_state, Err(err), chan_entry)
11545+
},
11546+
}
11547+
},
11548+
hash_map::Entry::Vacant(_) => (
11549+
Err(MsgHandleErrInternal::no_such_channel_for_peer(
11550+
counterparty_node_id,
11551+
msg.channel_id,
11552+
)),
11553+
Vec::new(),
11554+
),
11555+
}
11556+
};
11557+
11558+
self.handle_holding_cell_free_result(holding_cell_res);
11559+
result
1154411560
}
1154511561

1154611562
fn internal_tx_abort(

lightning/src/ln/splicing_tests.rs

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2099,6 +2099,95 @@ fn fail_splice_on_tx_complete_error() {
20992099
do_commitment_signed_dance(initiator, acceptor, &update.commitment_signed, false, false);
21002100
}
21012101

2102+
#[test]
2103+
fn free_holding_cell_on_tx_signatures_quiescence_exit() {
2104+
// Test that if there's an update in the holding cell while we're quiescent, that it gets freed
2105+
// upon exiting quiescence via the `tx_signatures` exchange.
2106+
let chanmon_cfgs = create_chanmon_cfgs(2);
2107+
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
2108+
let config = test_default_channel_config();
2109+
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[Some(config.clone()), Some(config)]);
2110+
let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
2111+
2112+
let initiator = &nodes[0];
2113+
let acceptor = &nodes[1];
2114+
let node_id_initiator = initiator.node.get_our_node_id();
2115+
let node_id_acceptor = acceptor.node.get_our_node_id();
2116+
2117+
let (_, _, channel_id, _) =
2118+
create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 100_000, 0);
2119+
2120+
let contribution = SpliceContribution::splice_out(vec![TxOut {
2121+
value: Amount::from_sat(1_000),
2122+
script_pubkey: initiator.wallet_source.get_change_script().unwrap(),
2123+
}]);
2124+
negotiate_splice_tx(initiator, acceptor, channel_id, contribution);
2125+
2126+
// Queue an outgoing HTLC to the holding cell. It should be freed once we exit quiescence.
2127+
let (route, payment_hash, _payment_preimage, payment_secret) =
2128+
get_route_and_payment_hash!(initiator, acceptor, 1_000_000);
2129+
let onion = RecipientOnionFields::secret_only(payment_secret);
2130+
let payment_id = PaymentId(payment_hash.0);
2131+
initiator.node.send_payment_with_route(route, payment_hash, onion, payment_id).unwrap();
2132+
assert!(initiator.node.get_and_clear_pending_msg_events().is_empty());
2133+
2134+
let event = get_event!(initiator, Event::FundingTransactionReadyForSigning);
2135+
if let Event::FundingTransactionReadyForSigning {
2136+
channel_id,
2137+
counterparty_node_id,
2138+
unsigned_transaction,
2139+
..
2140+
} = event
2141+
{
2142+
let partially_signed_tx = initiator.wallet_source.sign_tx(unsigned_transaction).unwrap();
2143+
initiator
2144+
.node
2145+
.funding_transaction_signed(&channel_id, &counterparty_node_id, partially_signed_tx)
2146+
.unwrap();
2147+
} else {
2148+
unreachable!();
2149+
}
2150+
2151+
let update = get_htlc_update_msgs(initiator, &node_id_acceptor);
2152+
acceptor.node.handle_commitment_signed(node_id_initiator, &update.commitment_signed[0]);
2153+
check_added_monitors(&acceptor, 1);
2154+
2155+
let msg_events = acceptor.node.get_and_clear_pending_msg_events();
2156+
assert_eq!(msg_events.len(), 2, "{msg_events:?}");
2157+
if let MessageSendEvent::UpdateHTLCs { ref updates, .. } = &msg_events[0] {
2158+
let commitment_signed = &updates.commitment_signed[0];
2159+
initiator.node.handle_commitment_signed(node_id_acceptor, commitment_signed);
2160+
check_added_monitors(&initiator, 1);
2161+
} else {
2162+
panic!("Unexpected event {:?}", &msg_events[0]);
2163+
}
2164+
if let MessageSendEvent::SendTxSignatures { ref msg, .. } = &msg_events[1] {
2165+
initiator.node.handle_tx_signatures(node_id_acceptor, msg);
2166+
} else {
2167+
panic!("Unexpected event {:?}", &msg_events[1]);
2168+
}
2169+
2170+
// With `tx_signatures` exchanged, we've exited quiescence and should now see the outgoing HTLC
2171+
// update be sent.
2172+
let msg_events = initiator.node.get_and_clear_pending_msg_events();
2173+
assert_eq!(msg_events.len(), 2, "{msg_events:?}");
2174+
check_added_monitors(initiator, 1); // Outgoing HTLC monitor update
2175+
if let MessageSendEvent::SendTxSignatures { ref msg, .. } = &msg_events[0] {
2176+
acceptor.node.handle_tx_signatures(node_id_initiator, msg);
2177+
} else {
2178+
panic!("Unexpected event {:?}", &msg_events[0]);
2179+
}
2180+
if let MessageSendEvent::UpdateHTLCs { updates, .. } = &msg_events[1] {
2181+
acceptor.node.handle_update_add_htlc(node_id_initiator, &updates.update_add_htlcs[0]);
2182+
do_commitment_signed_dance(acceptor, initiator, &updates.commitment_signed, false, false);
2183+
} else {
2184+
panic!("Unexpected event {:?}", &msg_events[1]);
2185+
}
2186+
2187+
expect_splice_pending_event(initiator, &node_id_acceptor);
2188+
expect_splice_pending_event(acceptor, &node_id_initiator);
2189+
}
2190+
21022191
#[test]
21032192
fn fail_splice_on_channel_close() {
21042193
let chanmon_cfgs = create_chanmon_cfgs(2);

0 commit comments

Comments
 (0)