Skip to content

Commit 95e7559

Browse files
committed
Add Custom TLVs for payment::ReceiveTlvs
- Building on the previous commit, this update allows users to include their own custom TLVs within the reply path of a sent onion message. - With this, users can attach custom data to the message, which will be returned in the response, providing more flexibility for custom use cases.
1 parent c256f30 commit 95e7559

File tree

7 files changed

+32
-5
lines changed

7 files changed

+32
-5
lines changed

fuzz/src/invoice_request_deser.rs

+1
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ fn build_response<T: secp256k1::Signing + secp256k1::Verification>(
9999
htlc_minimum_msat: 1,
100100
},
101101
payment_context,
102+
custom_data: Vec::new(),
102103
};
103104
let intermediate_nodes = [PaymentForwardNode {
104105
tlvs: ForwardTlvs {

fuzz/src/refund_deser.rs

+1
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ fn build_response<T: secp256k1::Signing + secp256k1::Verification>(
7676
htlc_minimum_msat: 1,
7777
},
7878
payment_context,
79+
custom_data: Vec::new(),
7980
};
8081
let intermediate_nodes = [PaymentForwardNode {
8182
tlvs: ForwardTlvs {

lightning/src/blinded_path/payment.rs

+18-1
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,15 @@ pub struct ReceiveTlvs {
260260
pub payment_constraints: PaymentConstraints,
261261
/// Context for the receiver of this payment.
262262
pub payment_context: PaymentContext,
263+
/// Custom data set by the user. And is returned back when the blinded path is used.
264+
///
265+
/// ## Note on Forward Compatibility:
266+
/// Users can encode any kind of data into the `Vec<u8>` bytes here. However, they should ensure
267+
/// that the data is structured in a forward-compatible manner. This is especially important as
268+
/// `ReceiveTlvs` created in one version of the software may still appear in payments received
269+
/// shortly after a software upgrade. Proper forward compatibility helps prevent data loss or
270+
/// misinterpretation in future versions.
271+
pub custom_data: Vec<u8>,
263272
}
264273

265274
/// Data to construct a [`BlindedHop`] for sending a payment over.
@@ -404,7 +413,8 @@ impl Writeable for ReceiveTlvs {
404413
encode_tlv_stream!(w, {
405414
(12, self.payment_constraints, required),
406415
(65536, self.payment_secret, required),
407-
(65537, self.payment_context, required)
416+
(65537, self.payment_context, required),
417+
(65539, self.custom_data, (default_value, Vec::new())),
408418
});
409419
Ok(())
410420
}
@@ -432,6 +442,7 @@ impl Readable for BlindedPaymentTlvs {
432442
(14, features, (option, encoding: (BlindedHopFeatures, WithoutLength))),
433443
(65536, payment_secret, option),
434444
(65537, payment_context, (default_value, PaymentContext::unknown())),
445+
(65539, custom_data, (default_value, Vec::new()))
435446
});
436447
let _padding: Option<utils::Padding> = _padding;
437448

@@ -452,6 +463,7 @@ impl Readable for BlindedPaymentTlvs {
452463
payment_secret: payment_secret.ok_or(DecodeError::InvalidValue)?,
453464
payment_constraints: payment_constraints.0.unwrap(),
454465
payment_context: payment_context.0.unwrap(),
466+
custom_data: custom_data.0.unwrap(),
455467
}))
456468
}
457469
}
@@ -683,6 +695,7 @@ mod tests {
683695
htlc_minimum_msat: 1,
684696
},
685697
payment_context: PaymentContext::unknown(),
698+
custom_data: Vec::new(),
686699
};
687700
let htlc_maximum_msat = 100_000;
688701
let blinded_payinfo = super::compute_payinfo(&intermediate_nodes[..], &recv_tlvs, htlc_maximum_msat, 12).unwrap();
@@ -702,6 +715,7 @@ mod tests {
702715
htlc_minimum_msat: 1,
703716
},
704717
payment_context: PaymentContext::unknown(),
718+
custom_data: Vec::new(),
705719
};
706720
let blinded_payinfo = super::compute_payinfo(&[], &recv_tlvs, 4242, TEST_FINAL_CLTV as u16).unwrap();
707721
assert_eq!(blinded_payinfo.fee_base_msat, 0);
@@ -758,6 +772,7 @@ mod tests {
758772
htlc_minimum_msat: 3,
759773
},
760774
payment_context: PaymentContext::unknown(),
775+
custom_data: Vec::new(),
761776
};
762777
let htlc_maximum_msat = 100_000;
763778
let blinded_payinfo = super::compute_payinfo(&intermediate_nodes[..], &recv_tlvs, htlc_maximum_msat, TEST_FINAL_CLTV as u16).unwrap();
@@ -811,6 +826,7 @@ mod tests {
811826
htlc_minimum_msat: 1,
812827
},
813828
payment_context: PaymentContext::unknown(),
829+
custom_data: Vec::new(),
814830
};
815831
let htlc_minimum_msat = 3798;
816832
assert!(super::compute_payinfo(&intermediate_nodes[..], &recv_tlvs, htlc_minimum_msat - 1, TEST_FINAL_CLTV as u16).is_err());
@@ -868,6 +884,7 @@ mod tests {
868884
htlc_minimum_msat: 1,
869885
},
870886
payment_context: PaymentContext::unknown(),
887+
custom_data: Vec::new(),
871888
};
872889

873890
let blinded_payinfo = super::compute_payinfo(&intermediate_nodes[..], &recv_tlvs, 10_000, TEST_FINAL_CLTV as u16).unwrap();

lightning/src/ln/blinded_payment_tests.rs

+4
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ fn blinded_payment_path(
7777
intro_node_min_htlc_opt.unwrap_or_else(|| channel_upds.last().unwrap().htlc_minimum_msat),
7878
},
7979
payment_context: PaymentContext::unknown(),
80+
custom_data: Vec::new(),
8081
};
8182
let mut secp_ctx = Secp256k1::new();
8283
BlindedPaymentPath::new(
@@ -123,6 +124,7 @@ fn do_one_hop_blinded_path(success: bool) {
123124
htlc_minimum_msat: chan_upd.htlc_minimum_msat,
124125
},
125126
payment_context: PaymentContext::unknown(),
127+
custom_data: Vec::new(),
126128
};
127129
let mut secp_ctx = Secp256k1::new();
128130
let blinded_path = BlindedPaymentPath::new(
@@ -167,6 +169,7 @@ fn mpp_to_one_hop_blinded_path() {
167169
htlc_minimum_msat: chan_upd_1_3.htlc_minimum_msat,
168170
},
169171
payment_context: PaymentContext::unknown(),
172+
custom_data: Vec::new(),
170173
};
171174
let blinded_path = BlindedPaymentPath::new(
172175
&[], nodes[3].node.get_our_node_id(), payee_tlvs, u64::MAX, TEST_FINAL_CLTV as u16,
@@ -1376,6 +1379,7 @@ fn sender_custom_tlvs_to_blinded_path() {
13761379
htlc_minimum_msat: chan_upd.htlc_minimum_msat,
13771380
},
13781381
payment_context: PaymentContext::unknown(),
1382+
custom_data: Vec::new(),
13791383
};
13801384
let mut secp_ctx = Secp256k1::new();
13811385
let blinded_path = BlindedPaymentPath::new(

lightning/src/ln/channelmanager.rs

+4-3
Original file line numberDiff line numberDiff line change
@@ -10193,7 +10193,7 @@ where
1019310193
Ok((payment_hash, payment_secret)) => {
1019410194
let payment_context = PaymentContext::Bolt12Refund(Bolt12RefundContext {});
1019510195
let payment_paths = self.create_blinded_payment_paths(
10196-
amount_msats, payment_secret, payment_context
10196+
amount_msats, payment_secret, payment_context, None
1019710197
)
1019810198
.map_err(|_| Bolt12SemanticError::MissingPaths)?;
1019910199

@@ -10512,7 +10512,7 @@ where
1051210512
/// Creates multi-hop blinded payment paths for the given `amount_msats` by delegating to
1051310513
/// [`Router::create_blinded_payment_paths`].
1051410514
fn create_blinded_payment_paths(
10515-
&self, amount_msats: u64, payment_secret: PaymentSecret, payment_context: PaymentContext
10515+
&self, amount_msats: u64, payment_secret: PaymentSecret, payment_context: PaymentContext, custom_data: Option<Vec<u8>>
1051610516
) -> Result<Vec<BlindedPaymentPath>, ()> {
1051710517
let secp_ctx = &self.secp_ctx;
1051810518

@@ -10527,6 +10527,7 @@ where
1052710527
htlc_minimum_msat: 1,
1052810528
},
1052910529
payment_context,
10530+
custom_data: custom_data.unwrap_or_default()
1053010531
};
1053110532
self.router.create_blinded_payment_paths(
1053210533
payee_node_id, first_hops, payee_tlvs, amount_msats, secp_ctx
@@ -12066,7 +12067,7 @@ where
1206612067
invoice_request: invoice_request.fields(),
1206712068
});
1206812069
let payment_paths = match self.create_blinded_payment_paths(
12069-
amount_msats, payment_secret, payment_context
12070+
amount_msats, payment_secret, payment_context, None
1207012071
) {
1207112072
Ok(payment_paths) => payment_paths,
1207212073
Err(()) => {

lightning/src/ln/max_payment_path_len_tests.rs

+1
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,7 @@ fn one_hop_blinded_path_with_custom_tlv() {
165165
htlc_minimum_msat: chan_upd_1_2.htlc_minimum_msat,
166166
},
167167
payment_context: PaymentContext::unknown(),
168+
custom_data: Vec::new(),
168169
};
169170
let mut secp_ctx = Secp256k1::new();
170171
let blinded_path = BlindedPaymentPath::new(

lightning/src/ln/msgs.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -2925,9 +2925,11 @@ impl<NS: Deref> ReadableArgs<(Option<PublicKey>, NS)> for InboundOnionPayload wh
29252925
next_blinding_override,
29262926
})
29272927
},
2928+
// Note: The custom data in the receive tlvs is not used here.
29282929
ChaChaPolyReadAdapter { readable: BlindedPaymentTlvs::Receive(ReceiveTlvs {
2929-
payment_secret, payment_constraints, payment_context
2930+
payment_secret, payment_constraints, payment_context, custom_data
29302931
})} => {
2932+
debug_assert_eq!(custom_data, user_custom_data, "The custom TLVs in ReceiveTlvs must match the ones read from serialization.");
29312933
if total_msat.unwrap_or(0) > MAX_VALUE_MSAT { return Err(DecodeError::InvalidValue) }
29322934
Ok(Self::BlindedReceive {
29332935
sender_intended_htlc_amt_msat: amt.ok_or(DecodeError::InvalidValue)?,

0 commit comments

Comments
 (0)