Skip to content

Commit 4d72cbe

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 6c028b0 commit 4d72cbe

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
@@ -11422,90 +11422,106 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
1142211422
fn internal_tx_signatures(
1142311423
&self, counterparty_node_id: &PublicKey, msg: &msgs::TxSignatures,
1142411424
) -> Result<(), MsgHandleErrInternal> {
11425-
let per_peer_state = self.per_peer_state.read().unwrap();
11426-
let peer_state_mutex = per_peer_state.get(counterparty_node_id).ok_or_else(|| {
11427-
debug_assert!(false);
11428-
MsgHandleErrInternal::no_such_peer(counterparty_node_id, msg.channel_id)
11429-
})?;
11430-
let mut peer_state_lock = peer_state_mutex.lock().unwrap();
11431-
let peer_state = &mut *peer_state_lock;
11432-
match peer_state.channel_by_id.entry(msg.channel_id) {
11433-
hash_map::Entry::Occupied(mut chan_entry) => {
11434-
match chan_entry.get_mut().as_funded_mut() {
11435-
Some(chan) => {
11436-
let best_block_height = self.best_block.read().unwrap().height;
11437-
let FundingTxSigned {
11438-
commitment_signed,
11439-
counterparty_initial_commitment_signed_result,
11440-
tx_signatures,
11441-
funding_tx,
11442-
splice_negotiated,
11443-
splice_locked,
11444-
} = try_channel_entry!(
11445-
self,
11446-
peer_state,
11447-
chan.tx_signatures(msg, best_block_height, &self.logger),
11448-
chan_entry
11449-
);
11450-
11451-
// We should never be sending a `commitment_signed` in response to their
11452-
// `tx_signatures`.
11453-
debug_assert!(commitment_signed.is_none());
11454-
debug_assert!(counterparty_initial_commitment_signed_result.is_none());
11455-
11456-
if let Some(tx_signatures) = tx_signatures {
11457-
peer_state.pending_msg_events.push(
11458-
MessageSendEvent::SendTxSignatures {
11459-
node_id: *counterparty_node_id,
11460-
msg: tx_signatures,
11461-
},
11462-
);
11463-
}
11464-
if let Some(splice_locked) = splice_locked {
11465-
peer_state.pending_msg_events.push(
11466-
MessageSendEvent::SendSpliceLocked {
11467-
node_id: *counterparty_node_id,
11468-
msg: splice_locked,
11469-
},
11470-
);
11471-
}
11472-
if let Some((ref funding_tx, ref tx_type)) = funding_tx {
11473-
self.broadcast_interactive_funding(
11474-
chan,
11425+
let (result, holding_cell_res) = {
11426+
let per_peer_state = self.per_peer_state.read().unwrap();
11427+
let peer_state_mutex = per_peer_state.get(counterparty_node_id).ok_or_else(|| {
11428+
debug_assert!(false);
11429+
MsgHandleErrInternal::no_such_peer(counterparty_node_id, msg.channel_id)
11430+
})?;
11431+
let mut peer_state_lock = peer_state_mutex.lock().unwrap();
11432+
let peer_state = &mut *peer_state_lock;
11433+
match peer_state.channel_by_id.entry(msg.channel_id) {
11434+
hash_map::Entry::Occupied(mut chan_entry) => {
11435+
match chan_entry.get_mut().as_funded_mut() {
11436+
Some(chan) => {
11437+
let best_block_height = self.best_block.read().unwrap().height;
11438+
let FundingTxSigned {
11439+
commitment_signed,
11440+
counterparty_initial_commitment_signed_result,
11441+
tx_signatures,
1147511442
funding_tx,
11476-
Some(tx_type.clone()),
11477-
&self.logger,
11443+
splice_negotiated,
11444+
splice_locked,
11445+
} = try_channel_entry!(
11446+
self,
11447+
peer_state,
11448+
chan.tx_signatures(msg, best_block_height, &self.logger),
11449+
chan_entry
1147811450
);
11479-
}
11480-
if let Some(splice_negotiated) = splice_negotiated {
11481-
self.pending_events.lock().unwrap().push_back((
11482-
events::Event::SplicePending {
11483-
channel_id: msg.channel_id,
11484-
counterparty_node_id: *counterparty_node_id,
11485-
user_channel_id: chan.context.get_user_id(),
11486-
new_funding_txo: splice_negotiated.funding_txo,
11487-
channel_type: splice_negotiated.channel_type,
11488-
new_funding_redeem_script: splice_negotiated
11489-
.funding_redeem_script,
11490-
},
11491-
None,
11492-
));
11493-
}
11494-
},
11495-
None => {
11496-
let msg = "Got an unexpected tx_signatures message";
11497-
let reason = ClosureReason::ProcessingError { err: msg.to_owned() };
11498-
let err = ChannelError::Close((msg.to_owned(), reason));
11499-
try_channel_entry!(self, peer_state, Err(err), chan_entry)
11500-
},
11501-
}
11502-
Ok(())
11503-
},
11504-
hash_map::Entry::Vacant(_) => Err(MsgHandleErrInternal::no_such_channel_for_peer(
11505-
counterparty_node_id,
11506-
msg.channel_id,
11507-
)),
11508-
}
11451+
11452+
// We should never be sending a `commitment_signed` in response to their
11453+
// `tx_signatures`.
11454+
debug_assert!(commitment_signed.is_none());
11455+
debug_assert!(counterparty_initial_commitment_signed_result.is_none());
11456+
11457+
if let Some(tx_signatures) = tx_signatures {
11458+
peer_state.pending_msg_events.push(
11459+
MessageSendEvent::SendTxSignatures {
11460+
node_id: *counterparty_node_id,
11461+
msg: tx_signatures,
11462+
},
11463+
);
11464+
}
11465+
if let Some(splice_locked) = splice_locked {
11466+
peer_state.pending_msg_events.push(
11467+
MessageSendEvent::SendSpliceLocked {
11468+
node_id: *counterparty_node_id,
11469+
msg: splice_locked,
11470+
},
11471+
);
11472+
}
11473+
if let Some((ref funding_tx, ref tx_type)) = funding_tx {
11474+
self.broadcast_interactive_funding(
11475+
chan,
11476+
funding_tx,
11477+
Some(tx_type.clone()),
11478+
&self.logger,
11479+
);
11480+
}
11481+
// We consider a splice negotiated when we exchange `tx_signatures`,
11482+
// which also terminates quiescence.
11483+
let exited_quiescence = splice_negotiated.is_some();
11484+
if let Some(splice_negotiated) = splice_negotiated {
11485+
self.pending_events.lock().unwrap().push_back((
11486+
events::Event::SplicePending {
11487+
channel_id: msg.channel_id,
11488+
counterparty_node_id: *counterparty_node_id,
11489+
user_channel_id: chan.context.get_user_id(),
11490+
new_funding_txo: splice_negotiated.funding_txo,
11491+
channel_type: splice_negotiated.channel_type,
11492+
new_funding_redeem_script: splice_negotiated
11493+
.funding_redeem_script,
11494+
},
11495+
None,
11496+
));
11497+
}
11498+
let holding_cell_res = if exited_quiescence {
11499+
self.check_free_peer_holding_cells(peer_state)
11500+
} else {
11501+
Vec::new()
11502+
};
11503+
(Ok(()), holding_cell_res)
11504+
},
11505+
None => {
11506+
let msg = "Got an unexpected tx_signatures message";
11507+
let reason = ClosureReason::ProcessingError { err: msg.to_owned() };
11508+
let err = ChannelError::Close((msg.to_owned(), reason));
11509+
try_channel_entry!(self, peer_state, Err(err), chan_entry)
11510+
},
11511+
}
11512+
},
11513+
hash_map::Entry::Vacant(_) => (
11514+
Err(MsgHandleErrInternal::no_such_channel_for_peer(
11515+
counterparty_node_id,
11516+
msg.channel_id,
11517+
)),
11518+
Vec::new(),
11519+
),
11520+
}
11521+
};
11522+
11523+
self.handle_holding_cell_free_result(holding_cell_res);
11524+
result
1150911525
}
1151011526

1151111527
fn internal_tx_abort(

lightning/src/ln/splicing_tests.rs

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

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

0 commit comments

Comments
 (0)