Skip to content

Commit 9855450

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 112522f commit 9855450

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

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