Skip to content

Commit b4fb555

Browse files
authored
Merge pull request #4312 from TheBlueMatt/2026-01-spurious-debug-assertion
Remove spurious debug assertion added in 0.2
2 parents c5d7b13 + b524b9b commit b4fb555

File tree

3 files changed

+153
-3
lines changed

3 files changed

+153
-3
lines changed

lightning/src/ln/channel.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8981,7 +8981,6 @@ where
89818981
update_fail_htlcs.len() + update_fail_malformed_htlcs.len(),
89828982
&self.context.channel_id);
89838983
} else {
8984-
debug_assert!(htlcs_to_fail.is_empty());
89858984
let reason = if self.context.channel_state.is_local_stfu_sent() {
89868985
"exits quiescence"
89878986
} else if self.context.channel_state.is_monitor_update_in_progress() {

lightning/src/ln/functional_test_utils.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -971,7 +971,7 @@ pub fn get_revoke_commit_msgs<CM: AChannelManager, H: NodeHolder<CM = CM>>(
971971
assert_eq!(node_id, recipient);
972972
(*msg).clone()
973973
},
974-
_ => panic!("Unexpected event"),
974+
_ => panic!("Unexpected event: {events:?}"),
975975
},
976976
match events[1] {
977977
MessageSendEvent::UpdateHTLCs { ref node_id, ref channel_id, ref updates } => {
@@ -984,7 +984,7 @@ pub fn get_revoke_commit_msgs<CM: AChannelManager, H: NodeHolder<CM = CM>>(
984984
assert!(updates.commitment_signed.iter().all(|cs| cs.channel_id == *channel_id));
985985
updates.commitment_signed.clone()
986986
},
987-
_ => panic!("Unexpected event"),
987+
_ => panic!("Unexpected event: {events:?}"),
988988
},
989989
)
990990
}

lightning/src/ln/functional_tests.rs

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9900,3 +9900,154 @@ pub fn test_multi_post_event_actions() {
99009900
do_test_multi_post_event_actions(true);
99019901
do_test_multi_post_event_actions(false);
99029902
}
9903+
9904+
#[xtest(feature = "_externalize_tests")]
9905+
pub fn test_dust_exposure_holding_cell_assertion() {
9906+
// Test that we properly move forward if we pop an HTLC-add from the holding cell but fail to
9907+
// add it to the channel. In 0.2 this cause a (harmless in prod) debug assertion failure. We
9908+
// try to ensure that this won't happen by checking that an HTLC will be able to be added
9909+
// before we add it to the holding cell, so getting into this state takes a bit of work.
9910+
//
9911+
// Here we accomplish this by using the dust exposure limit. This has the unique feature that
9912+
// node C can increase node B's dust exposure on the B <-> C channel without B doing anything.
9913+
// To exploit this, we get node B one HTLC away from being over-exposed to dust, give it one
9914+
// more HTLC in the holding cell, then have node C add an HTLC. By the time the holding-cell
9915+
// HTLC is released we are at max-dust-exposure and will fail it.
9916+
9917+
let chanmon_cfgs = create_chanmon_cfgs(3);
9918+
let node_cfgs = create_node_cfgs(3, &chanmon_cfgs);
9919+
9920+
// Configure nodes with specific dust limits
9921+
let mut config = test_default_channel_config();
9922+
// Use a fixed dust exposure limit to make the test simpler
9923+
const DUST_HTLC_VALUE_MSAT: u64 = 500_000;
9924+
config.channel_config.max_dust_htlc_exposure = MaxDustHTLCExposure::FixedLimitMsat(5_000_000);
9925+
config.channel_handshake_config.max_inbound_htlc_value_in_flight_percent_of_channel = 100;
9926+
9927+
let configs = [Some(config.clone()), Some(config.clone()), Some(config.clone())];
9928+
let node_chanmgrs = create_node_chanmgrs(3, &node_cfgs, &configs);
9929+
let nodes = create_network(3, &node_cfgs, &node_chanmgrs);
9930+
9931+
let node_a_id = nodes[0].node.get_our_node_id();
9932+
let node_b_id = nodes[1].node.get_our_node_id();
9933+
let node_c_id = nodes[2].node.get_our_node_id();
9934+
9935+
// Create channels: A <-> B <-> C
9936+
create_announced_chan_between_nodes(&nodes, 0, 1);
9937+
let bc_chan_id = create_announced_chan_between_nodes(&nodes, 1, 2).2;
9938+
send_payment(&nodes[0], &[&nodes[1], &nodes[2]], 10_000_000);
9939+
9940+
// Send multiple dust HTLCs from B to C to approach the dust limit (including transaction fees)
9941+
for _ in 0..4 {
9942+
route_payment(&nodes[1], &[&nodes[2]], DUST_HTLC_VALUE_MSAT);
9943+
}
9944+
9945+
// At this point we shouldn't be over the dust limit, and should still be able to send HTLCs.
9946+
let bs_chans = nodes[1].node.list_channels();
9947+
let bc_chan = bs_chans.iter().find(|chan| chan.counterparty.node_id == node_c_id).unwrap();
9948+
assert_eq!(
9949+
bc_chan.next_outbound_htlc_minimum_msat,
9950+
config.channel_handshake_config.our_htlc_minimum_msat
9951+
);
9952+
9953+
// Add a further HTLC from B to C, but don't deliver the send messages.
9954+
// After this we'll only have the ability to add one more HTLC, but by not delivering the send
9955+
// messages (leaving B waiting on C's RAA) the next HTLC will go into B's holding cell.
9956+
let (route_bc, payment_hash_bc, _payment_preimage_bc, payment_secret_bc) =
9957+
get_route_and_payment_hash!(nodes[1], nodes[2], DUST_HTLC_VALUE_MSAT);
9958+
let onion_bc = RecipientOnionFields::secret_only(payment_secret_bc);
9959+
let id = PaymentId(payment_hash_bc.0);
9960+
nodes[1].node.send_payment_with_route(route_bc, payment_hash_bc, onion_bc, id).unwrap();
9961+
check_added_monitors(&nodes[1], 1);
9962+
let send_bc = SendEvent::from_node(&nodes[1]);
9963+
9964+
let bs_chans = nodes[1].node.list_channels();
9965+
let bc_chan = bs_chans.iter().find(|chan| chan.counterparty.node_id == node_c_id).unwrap();
9966+
assert_eq!(
9967+
bc_chan.next_outbound_htlc_minimum_msat,
9968+
config.channel_handshake_config.our_htlc_minimum_msat
9969+
);
9970+
9971+
// Forward an additional HTLC from A through B to C. This will go in B's holding cell for node
9972+
// C as it is waiting on a response to the above messages.
9973+
let payment_params_ac = PaymentParameters::from_node_id(node_c_id, TEST_FINAL_CLTV)
9974+
.with_bolt11_features(nodes[2].node.bolt11_invoice_features())
9975+
.unwrap();
9976+
let (route_ac, payment_hash_cell, _, payment_secret_ac) =
9977+
get_route_and_payment_hash!(nodes[0], nodes[2], payment_params_ac, DUST_HTLC_VALUE_MSAT);
9978+
let onion_ac = RecipientOnionFields::secret_only(payment_secret_ac);
9979+
let id = PaymentId(payment_hash_cell.0);
9980+
nodes[0].node.send_payment_with_route(route_ac, payment_hash_cell, onion_ac, id).unwrap();
9981+
check_added_monitors(&nodes[0], 1);
9982+
9983+
let send_ab = SendEvent::from_node(&nodes[0]);
9984+
nodes[1].node.handle_update_add_htlc(node_a_id, &send_ab.msgs[0]);
9985+
do_commitment_signed_dance(&nodes[1], &nodes[0], &send_ab.commitment_msg, false, true);
9986+
9987+
// At this point when we process pending forwards the HTLC will go into the holding cell and no
9988+
// further messages will be generated. Node B will also be at its maximum dust exposure and
9989+
// will refuse to send any dust HTLCs (when it includes the holding cell HTLC).
9990+
expect_and_process_pending_htlcs(&nodes[1], false);
9991+
assert!(nodes[1].node.get_and_clear_pending_msg_events().is_empty());
9992+
9993+
let bs_chans = nodes[1].node.list_channels();
9994+
let bc_chan = bs_chans.iter().find(|chan| chan.counterparty.node_id == node_c_id).unwrap();
9995+
assert!(bc_chan.next_outbound_htlc_minimum_msat > DUST_HTLC_VALUE_MSAT);
9996+
9997+
// Send an additional HTLC from C to B. This will make B unable to forward the HTLC already in
9998+
// its holding cell as it would be over-exposed to dust.
9999+
let (route_cb, payment_hash_cb, payment_preimage_cb, payment_secret_cb) =
10000+
get_route_and_payment_hash!(nodes[2], nodes[1], DUST_HTLC_VALUE_MSAT);
10001+
let onion_cb = RecipientOnionFields::secret_only(payment_secret_cb);
10002+
let id = PaymentId(payment_hash_cb.0);
10003+
nodes[2].node.send_payment_with_route(route_cb, payment_hash_cb, onion_cb, id).unwrap();
10004+
check_added_monitors(&nodes[2], 1);
10005+
10006+
// Now deliver all the messages and make sure that the HTLC is failed-back.
10007+
let send_event_cb = SendEvent::from_node(&nodes[2]);
10008+
nodes[1].node.handle_update_add_htlc(node_c_id, &send_event_cb.msgs[0]);
10009+
nodes[1].node.handle_commitment_signed_batch_test(node_c_id, &send_event_cb.commitment_msg);
10010+
check_added_monitors(&nodes[1], 1);
10011+
10012+
nodes[2].node.handle_update_add_htlc(node_b_id, &send_bc.msgs[0]);
10013+
nodes[2].node.handle_commitment_signed_batch_test(node_b_id, &send_bc.commitment_msg);
10014+
check_added_monitors(&nodes[2], 1);
10015+
10016+
let cs_raa = get_event_msg!(nodes[2], MessageSendEvent::SendRevokeAndACK, node_b_id);
10017+
nodes[1].node.handle_revoke_and_ack(node_c_id, &cs_raa);
10018+
check_added_monitors(&nodes[1], 1);
10019+
let (bs_raa, bs_cs) = get_revoke_commit_msgs(&nodes[1], &node_c_id);
10020+
10021+
// When we delivered the RAA above, we attempted (and failed) to add the HTLC to the channel,
10022+
// causing it to be ready to fail-back, which we do here:
10023+
let next_hop =
10024+
HTLCHandlingFailureType::Forward { node_id: Some(node_c_id), channel_id: bc_chan_id };
10025+
expect_htlc_forwarding_fails(&nodes[1], &[next_hop]);
10026+
check_added_monitors(&nodes[1], 1);
10027+
fail_payment_along_path(&[&nodes[0], &nodes[1]]);
10028+
let conditions = PaymentFailedConditions::new();
10029+
expect_payment_failed_conditions(&nodes[0], payment_hash_cell, false, conditions);
10030+
10031+
nodes[2].node.handle_revoke_and_ack(node_b_id, &bs_raa);
10032+
check_added_monitors(&nodes[2], 1);
10033+
let cs_cs = get_htlc_update_msgs(&nodes[2], &node_b_id);
10034+
10035+
nodes[2].node.handle_commitment_signed_batch_test(node_b_id, &bs_cs);
10036+
check_added_monitors(&nodes[2], 1);
10037+
let cs_raa = get_event_msg!(nodes[2], MessageSendEvent::SendRevokeAndACK, node_b_id);
10038+
10039+
nodes[1].node.handle_commitment_signed_batch_test(node_c_id, &cs_cs.commitment_signed);
10040+
check_added_monitors(&nodes[1], 1);
10041+
let bs_raa = get_event_msg!(nodes[1], MessageSendEvent::SendRevokeAndACK, node_c_id);
10042+
10043+
nodes[1].node.handle_revoke_and_ack(node_c_id, &cs_raa);
10044+
check_added_monitors(&nodes[1], 1);
10045+
expect_and_process_pending_htlcs(&nodes[1], false);
10046+
expect_payment_claimable!(nodes[1], payment_hash_cb, payment_secret_cb, DUST_HTLC_VALUE_MSAT);
10047+
10048+
nodes[2].node.handle_revoke_and_ack(node_b_id, &bs_raa);
10049+
check_added_monitors(&nodes[2], 1);
10050+
10051+
// Now that everything has settled, make sure the channels still work with a simple claim.
10052+
claim_payment(&nodes[2], &[&nodes[1]], payment_preimage_cb);
10053+
}

0 commit comments

Comments
 (0)