From 7d68b608c8269c6b50d61c169c57eb0a203a4e01 Mon Sep 17 00:00:00 2001 From: Valentine Wallace Date: Wed, 27 Mar 2024 15:16:08 -0400 Subject: [PATCH 1/3] Support sending custom TLVs to blinded recipients. --- lightning/src/ln/msgs.rs | 14 ++++++++++---- lightning/src/ln/onion_utils.rs | 1 + 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/lightning/src/ln/msgs.rs b/lightning/src/ln/msgs.rs index 2efc3c1c051..89dff492067 100644 --- a/lightning/src/ln/msgs.rs +++ b/lightning/src/ln/msgs.rs @@ -1737,6 +1737,7 @@ mod fuzzy_internal_msgs { encrypted_tlvs: Vec, intro_node_blinding_point: Option, // Set if the introduction node of the blinded path is the final node keysend_preimage: Option, + custom_tlvs: Vec<(u64, Vec)>, } } @@ -2581,16 +2582,21 @@ impl Writeable for OutboundOnionPayload { }, Self::BlindedReceive { sender_intended_htlc_amt_msat, total_msat, cltv_expiry_height, encrypted_tlvs, - intro_node_blinding_point, keysend_preimage, + intro_node_blinding_point, keysend_preimage, ref custom_tlvs, } => { + // We need to update [`ln::outbound_payment::RecipientOnionFields::with_custom_tlvs`] + // to reject any reserved types in the experimental range if new ones are ever + // standardized. + let keysend_tlv = keysend_preimage.map(|preimage| (5482373484, preimage.encode())); + let mut custom_tlvs: Vec<&(u64, Vec)> = custom_tlvs.iter().chain(keysend_tlv.iter()).collect(); + custom_tlvs.sort_unstable_by_key(|(typ, _)| *typ); _encode_varint_length_prefixed_tlv!(w, { (2, HighZeroBytesDroppedBigSize(*sender_intended_htlc_amt_msat), required), (4, HighZeroBytesDroppedBigSize(*cltv_expiry_height), required), (10, *encrypted_tlvs, required_vec), (12, intro_node_blinding_point, option), - (18, HighZeroBytesDroppedBigSize(*total_msat), required), - (5482373484, keysend_preimage, option) - }); + (18, HighZeroBytesDroppedBigSize(*total_msat), required) + }, custom_tlvs.iter()); }, } Ok(()) diff --git a/lightning/src/ln/onion_utils.rs b/lightning/src/ln/onion_utils.rs index 53ef0729aa1..6742d0ae2af 100644 --- a/lightning/src/ln/onion_utils.rs +++ b/lightning/src/ln/onion_utils.rs @@ -214,6 +214,7 @@ pub(super) fn build_onion_payloads( encrypted_tlvs: blinded_hop.encrypted_payload.clone(), intro_node_blinding_point: blinding_point.take(), keysend_preimage: *keysend_preimage, + custom_tlvs: recipient_onion.custom_tlvs.clone(), }); } else { res.push(msgs::OutboundOnionPayload::BlindedForward { From 143b8b361580bcb47b2d7f499a9dcc78cb126f8b Mon Sep 17 00:00:00 2001 From: Valentine Wallace Date: Wed, 27 Mar 2024 16:41:25 -0400 Subject: [PATCH 2/3] Support receiving custom TLVs to blinded paths. --- lightning/src/ln/msgs.rs | 2 ++ lightning/src/ln/onion_payment.rs | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/lightning/src/ln/msgs.rs b/lightning/src/ln/msgs.rs index 89dff492067..2088c51fb5b 100644 --- a/lightning/src/ln/msgs.rs +++ b/lightning/src/ln/msgs.rs @@ -1701,6 +1701,7 @@ mod fuzzy_internal_msgs { payment_constraints: PaymentConstraints, intro_node_blinding_point: Option, keysend_preimage: Option, + custom_tlvs: Vec<(u64, Vec)>, } } @@ -2683,6 +2684,7 @@ impl ReadableArgs<(Option, &NS)> for InboundOnionPayload w payment_constraints, intro_node_blinding_point, keysend_preimage, + custom_tlvs, }) }, } diff --git a/lightning/src/ln/onion_payment.rs b/lightning/src/ln/onion_payment.rs index 8a721a8614b..a5560e28020 100644 --- a/lightning/src/ln/onion_payment.rs +++ b/lightning/src/ln/onion_payment.rs @@ -139,7 +139,7 @@ pub(super) fn create_recv_pending_htlc_info( cltv_expiry_height, payment_metadata, false), msgs::InboundOnionPayload::BlindedReceive { sender_intended_htlc_amt_msat, total_msat, cltv_expiry_height, payment_secret, - intro_node_blinding_point, payment_constraints, keysend_preimage, .. + intro_node_blinding_point, payment_constraints, keysend_preimage, custom_tlvs } => { check_blinded_payment_constraints( sender_intended_htlc_amt_msat, cltv_expiry, &payment_constraints @@ -152,7 +152,7 @@ pub(super) fn create_recv_pending_htlc_info( } })?; let payment_data = msgs::FinalOnionHopData { payment_secret, total_msat }; - (Some(payment_data), keysend_preimage, Vec::new(), + (Some(payment_data), keysend_preimage, custom_tlvs, sender_intended_htlc_amt_msat, cltv_expiry_height, None, intro_node_blinding_point.is_none()) } From cb022c5cbbc5de65925823bf22c22bc1b7f2d743 Mon Sep 17 00:00:00 2001 From: Valentine Wallace Date: Wed, 27 Mar 2024 16:41:40 -0400 Subject: [PATCH 3/3] Test sending + receiving custom TLVs to blinded paths. --- lightning/src/ln/blinded_payment_tests.rs | 47 +++++++++++++++++++++++ lightning/src/ln/functional_test_utils.rs | 10 ++++- lightning/src/ln/payment_tests.rs | 13 +++---- 3 files changed, 60 insertions(+), 10 deletions(-) diff --git a/lightning/src/ln/blinded_payment_tests.rs b/lightning/src/ln/blinded_payment_tests.rs index 198ded1cb44..eb31be9eecf 100644 --- a/lightning/src/ln/blinded_payment_tests.rs +++ b/lightning/src/ln/blinded_payment_tests.rs @@ -1264,3 +1264,50 @@ fn blinded_mpp_keysend() { Some(payment_secret), ev.clone(), true, Some(keysend_preimage)); claim_payment_along_route(&nodes[0], expected_route, false, keysend_preimage); } + +#[test] +fn custom_tlvs_to_blinded_path() { + let chanmon_cfgs = create_chanmon_cfgs(2); + let node_cfgs = create_node_cfgs(2, &chanmon_cfgs); + let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]); + let nodes = create_network(2, &node_cfgs, &node_chanmgrs); + let chan_upd = create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 1_000_000, 0).0.contents; + + let amt_msat = 5000; + let (payment_preimage, payment_hash, payment_secret) = get_payment_preimage_hash(&nodes[1], Some(amt_msat), None); + let payee_tlvs = ReceiveTlvs { + payment_secret, + payment_constraints: PaymentConstraints { + max_cltv_expiry: u32::max_value(), + htlc_minimum_msat: chan_upd.htlc_minimum_msat, + }, + }; + let mut secp_ctx = Secp256k1::new(); + let blinded_path = BlindedPath::one_hop_for_payment( + nodes[1].node.get_our_node_id(), payee_tlvs, TEST_FINAL_CLTV as u16, + &chanmon_cfgs[1].keys_manager, &secp_ctx + ).unwrap(); + + let route_params = RouteParameters::from_payment_params_and_value( + PaymentParameters::blinded(vec![blinded_path]), + amt_msat, + ); + + let recipient_onion_fields = RecipientOnionFields::spontaneous_empty() + .with_custom_tlvs(vec![((1 << 16) + 1, vec![42, 42])]) + .unwrap(); + nodes[0].node.send_payment(payment_hash, recipient_onion_fields.clone(), + PaymentId(payment_hash.0), route_params, Retry::Attempts(0)).unwrap(); + check_added_monitors(&nodes[0], 1); + + let mut events = nodes[0].node.get_and_clear_pending_msg_events(); + assert_eq!(events.len(), 1); + let ev = remove_first_msg_event_to_node(&nodes[1].node.get_our_node_id(), &mut events); + + let path = &[&nodes[1]]; + let args = PassAlongPathArgs::new(&nodes[0], path, amt_msat, payment_hash, ev) + .with_payment_secret(payment_secret) + .with_custom_tlvs(recipient_onion_fields.custom_tlvs.clone()); + do_pass_along_path(args); + claim_payment(&nodes[0], &[&nodes[1]], payment_preimage); +} diff --git a/lightning/src/ln/functional_test_utils.rs b/lightning/src/ln/functional_test_utils.rs index 5840fead943..b6a65c705e9 100644 --- a/lightning/src/ln/functional_test_utils.rs +++ b/lightning/src/ln/functional_test_utils.rs @@ -2512,6 +2512,7 @@ pub struct PassAlongPathArgs<'a, 'b, 'c, 'd> { pub clear_recipient_events: bool, pub expected_preimage: Option, pub is_probe: bool, + pub custom_tlvs: Vec<(u64, Vec)>, } impl<'a, 'b, 'c, 'd> PassAlongPathArgs<'a, 'b, 'c, 'd> { @@ -2522,7 +2523,7 @@ impl<'a, 'b, 'c, 'd> PassAlongPathArgs<'a, 'b, 'c, 'd> { Self { origin_node, expected_path, recv_value, payment_hash, payment_secret: None, event, payment_claimable_expected: true, clear_recipient_events: true, expected_preimage: None, - is_probe: false, + is_probe: false, custom_tlvs: Vec::new(), } } pub fn without_clearing_recipient_events(mut self) -> Self { @@ -2546,13 +2547,17 @@ impl<'a, 'b, 'c, 'd> PassAlongPathArgs<'a, 'b, 'c, 'd> { self.expected_preimage = Some(payment_preimage); self } + pub fn with_custom_tlvs(mut self, custom_tlvs: Vec<(u64, Vec)>) -> Self { + self.custom_tlvs = custom_tlvs; + self + } } pub fn do_pass_along_path<'a, 'b, 'c>(args: PassAlongPathArgs) -> Option { let PassAlongPathArgs { origin_node, expected_path, recv_value, payment_hash: our_payment_hash, payment_secret: our_payment_secret, event: ev, payment_claimable_expected, - clear_recipient_events, expected_preimage, is_probe + clear_recipient_events, expected_preimage, is_probe, custom_tlvs } = args; let mut payment_event = SendEvent::from_event(ev); @@ -2585,6 +2590,7 @@ pub fn do_pass_along_path<'a, 'b, 'c>(args: PassAlongPathArgs) -> Option assert_eq!(our_payment_hash, *payment_hash); assert_eq!(node.node.get_our_node_id(), receiver_node_id.unwrap()); assert!(onion_fields.is_some()); + assert_eq!(onion_fields.as_ref().unwrap().custom_tlvs, custom_tlvs); match &purpose { PaymentPurpose::InvoicePayment { payment_preimage, payment_secret, .. } => { assert_eq!(expected_preimage, *payment_preimage); diff --git a/lightning/src/ln/payment_tests.rs b/lightning/src/ln/payment_tests.rs index 09c022a4c87..a75120797ca 100644 --- a/lightning/src/ln/payment_tests.rs +++ b/lightning/src/ln/payment_tests.rs @@ -3813,14 +3813,11 @@ fn test_retry_custom_tlvs() { check_added_monitors!(nodes[0], 1); let mut events = nodes[0].node.get_and_clear_pending_msg_events(); assert_eq!(events.len(), 1); - let payment_claimable = pass_along_path(&nodes[0], &[&nodes[1], &nodes[2]], 1_000_000, - payment_hash, Some(payment_secret), events.pop().unwrap(), true, None).unwrap(); - match payment_claimable { - Event::PaymentClaimable { onion_fields, .. } => { - assert_eq!(&onion_fields.unwrap().custom_tlvs()[..], &custom_tlvs[..]); - }, - _ => panic!("Unexpected event"), - }; + let path = &[&nodes[1], &nodes[2]]; + let args = PassAlongPathArgs::new(&nodes[0], path, 1_000_000, payment_hash, events.pop().unwrap()) + .with_payment_secret(payment_secret) + .with_custom_tlvs(custom_tlvs); + do_pass_along_path(args); claim_payment_along_route(&nodes[0], &[&[&nodes[1], &nodes[2]]], false, payment_preimage); }