Skip to content

Commit 2d993b0

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 c3eaba9 commit 2d993b0

2 files changed

Lines changed: 187 additions & 82 deletions

File tree

lightning/src/ln/channelmanager.rs

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

1154811564
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)