Skip to content

Commit 64e4201

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 e446031 commit 64e4201

8 files changed

+40
-8
lines changed

fuzz/src/invoice_request_deser.rs

+1
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ fn build_response<T: secp256k1::Signing + secp256k1::Verification>(
103103
htlc_minimum_msat: 1,
104104
},
105105
payment_context,
106+
custom_data: None,
106107
};
107108
let payee_tlvs = payee_tlvs.authenticate(nonce, &expanded_key);
108109
let intermediate_nodes = [PaymentForwardNode {

fuzz/src/refund_deser.rs

+1
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ fn build_response<T: secp256k1::Signing + secp256k1::Verification>(
8080
htlc_minimum_msat: 1,
8181
},
8282
payment_context,
83+
custom_data: None,
8384
};
8485
let payee_tlvs = payee_tlvs.authenticate(nonce, &expanded_key);
8586
let intermediate_nodes = [PaymentForwardNode {

lightning/src/blinded_path/payment.rs

+18
Original file line numberDiff line numberDiff line change
@@ -325,6 +325,15 @@ pub struct UnauthenticatedReceiveTlvs {
325325
pub payment_constraints: PaymentConstraints,
326326
/// Context for the receiver of this payment.
327327
pub payment_context: PaymentContext,
328+
/// Custom data set by the user. And is returned back when the blinded path is used.
329+
///
330+
/// ## Note on Forward Compatibility:
331+
/// Users can encode any kind of data into the `Vec<u8>` bytes here. However, they should ensure
332+
/// that the data is structured in a forward-compatible manner. This is especially important as
333+
/// `ReceiveTlvs` created in one version of the software may still appear in payments received
334+
/// shortly after a software upgrade. Proper forward compatibility helps prevent data loss or
335+
/// misinterpretation in future versions.
336+
pub custom_data: Option<Vec<u8>>,
328337
}
329338

330339
impl UnauthenticatedReceiveTlvs {
@@ -490,6 +499,7 @@ impl Writeable for ReceiveTlvs {
490499
(65536, self.tlvs.payment_secret, required),
491500
(65537, self.tlvs.payment_context, required),
492501
(65539, self.authentication, required),
502+
(65541, self.tlvs.custom_data, required)
493503
});
494504
Ok(())
495505
}
@@ -501,6 +511,7 @@ impl Writeable for UnauthenticatedReceiveTlvs {
501511
(12, self.payment_constraints, required),
502512
(65536, self.payment_secret, required),
503513
(65537, self.payment_context, required),
514+
(65541, self.custom_data, (default_value, Vec::new())),
504515
});
505516
Ok(())
506517
}
@@ -529,6 +540,7 @@ impl Readable for BlindedPaymentTlvs {
529540
(65536, payment_secret, option),
530541
(65537, payment_context, option),
531542
(65539, authentication, option),
543+
(65541, custom_data, option)
532544
});
533545
let _padding: Option<utils::Padding> = _padding;
534546

@@ -552,6 +564,7 @@ impl Readable for BlindedPaymentTlvs {
552564
payment_secret: payment_secret.ok_or(DecodeError::InvalidValue)?,
553565
payment_constraints: payment_constraints.0.unwrap(),
554566
payment_context: payment_context.ok_or(DecodeError::InvalidValue)?,
567+
custom_data: custom_data.ok_or(DecodeError::InvalidValue)?,
555568
},
556569
authentication: authentication.ok_or(DecodeError::InvalidValue)?,
557570
}))
@@ -794,6 +807,7 @@ mod tests {
794807
payment_secret: PaymentSecret([0; 32]),
795808
payment_constraints: PaymentConstraints { max_cltv_expiry: 0, htlc_minimum_msat: 1 },
796809
payment_context: PaymentContext::Bolt12Refund(Bolt12RefundContext {}),
810+
custom_data: None,
797811
};
798812
let htlc_maximum_msat = 100_000;
799813
let blinded_payinfo =
@@ -812,6 +826,7 @@ mod tests {
812826
payment_secret: PaymentSecret([0; 32]),
813827
payment_constraints: PaymentConstraints { max_cltv_expiry: 0, htlc_minimum_msat: 1 },
814828
payment_context: PaymentContext::Bolt12Refund(Bolt12RefundContext {}),
829+
custom_data: None,
815830
};
816831
let blinded_payinfo =
817832
super::compute_payinfo(&[], &recv_tlvs, 4242, TEST_FINAL_CLTV as u16).unwrap();
@@ -869,6 +884,7 @@ mod tests {
869884
payment_secret: PaymentSecret([0; 32]),
870885
payment_constraints: PaymentConstraints { max_cltv_expiry: 0, htlc_minimum_msat: 3 },
871886
payment_context: PaymentContext::Bolt12Refund(Bolt12RefundContext {}),
887+
custom_data: None,
872888
};
873889
let htlc_maximum_msat = 100_000;
874890
let blinded_payinfo = super::compute_payinfo(
@@ -928,6 +944,7 @@ mod tests {
928944
payment_secret: PaymentSecret([0; 32]),
929945
payment_constraints: PaymentConstraints { max_cltv_expiry: 0, htlc_minimum_msat: 1 },
930946
payment_context: PaymentContext::Bolt12Refund(Bolt12RefundContext {}),
947+
custom_data: None,
931948
};
932949
let htlc_minimum_msat = 3798;
933950
assert!(super::compute_payinfo(
@@ -997,6 +1014,7 @@ mod tests {
9971014
payment_secret: PaymentSecret([0; 32]),
9981015
payment_constraints: PaymentConstraints { max_cltv_expiry: 0, htlc_minimum_msat: 1 },
9991016
payment_context: PaymentContext::Bolt12Refund(Bolt12RefundContext {}),
1017+
custom_data: None,
10001018
};
10011019

10021020
let blinded_payinfo = super::compute_payinfo(

lightning/src/ln/async_payments_tests.rs

+1
Original file line numberDiff line numberDiff line change
@@ -773,6 +773,7 @@ fn reject_bad_payment_secret() {
773773
// We don't reach the point of checking the invreq nonce due to the invalid payment secret
774774
offer_nonce: Nonce([i; Nonce::LENGTH]),
775775
}),
776+
None,
776777
u32::MAX,
777778
)
778779
.unwrap();

lightning/src/ln/blinded_payment_tests.rs

+8
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ pub fn blinded_payment_path(
7979
intro_node_min_htlc_opt.unwrap_or_else(|| channel_upds.last().unwrap().htlc_minimum_msat),
8080
},
8181
payment_context: PaymentContext::Bolt12Refund(Bolt12RefundContext {}),
82+
custom_data: None,
8283
};
8384

8485
let nonce = Nonce([42u8; 16]);
@@ -165,6 +166,7 @@ fn do_one_hop_blinded_path(success: bool) {
165166
htlc_minimum_msat: chan_upd.htlc_minimum_msat,
166167
},
167168
payment_context: PaymentContext::Bolt12Refund(Bolt12RefundContext {}),
169+
custom_data: None,
168170
};
169171
let nonce = Nonce([42u8; 16]);
170172
let expanded_key = chanmon_cfgs[1].keys_manager.get_inbound_payment_key();
@@ -213,6 +215,7 @@ fn mpp_to_one_hop_blinded_path() {
213215
htlc_minimum_msat: chan_upd_1_3.htlc_minimum_msat,
214216
},
215217
payment_context: PaymentContext::Bolt12Refund(Bolt12RefundContext {}),
218+
custom_data: None,
216219
};
217220
let nonce = Nonce([42u8; 16]);
218221
let expanded_key = chanmon_cfgs[3].keys_manager.get_inbound_payment_key();
@@ -878,6 +881,8 @@ fn do_multi_hop_receiver_fail(check: ReceiveCheckFail) {
878881
nodes.iter().skip(1).map(|n| n.node.get_our_node_id()).collect(), &[&chan_upd_1_2],
879882
&chanmon_cfgs[2].keys_manager);
880883

884+
route_params.payment_params.max_path_length = 18;
885+
881886
let route = if check == ReceiveCheckFail::ProcessPendingHTLCsCheck {
882887
let mut route = get_route(&nodes[0], &route_params).unwrap();
883888
// Set the final CLTV expiry too low to trigger the failure in process_pending_htlc_forwards.
@@ -1299,6 +1304,7 @@ fn sender_custom_tlvs_to_blinded_path() {
12991304
htlc_minimum_msat: chan_upd.htlc_minimum_msat,
13001305
},
13011306
payment_context: PaymentContext::Bolt12Refund(Bolt12RefundContext {}),
1307+
custom_data: None,
13021308
};
13031309
let nonce = Nonce([42u8; 16]);
13041310
let expanded_key = chanmon_cfgs[1].keys_manager.get_inbound_payment_key();
@@ -1353,6 +1359,7 @@ fn fails_receive_tlvs_authentication() {
13531359
htlc_minimum_msat: chan_upd.htlc_minimum_msat,
13541360
},
13551361
payment_context: PaymentContext::Bolt12Refund(Bolt12RefundContext {}),
1362+
custom_data: None,
13561363
};
13571364
let nonce = Nonce([42u8; 16]);
13581365
let expanded_key = chanmon_cfgs[1].keys_manager.get_inbound_payment_key();
@@ -1384,6 +1391,7 @@ fn fails_receive_tlvs_authentication() {
13841391
htlc_minimum_msat: chan_upd.htlc_minimum_msat,
13851392
},
13861393
payment_context: PaymentContext::Bolt12Refund(Bolt12RefundContext {}),
1394+
custom_data: None,
13871395
};
13881396
let nonce = Nonce([43u8; 16]);
13891397
let mut payee_tlvs = payee_tlvs.authenticate(nonce, &expanded_key);

lightning/src/ln/channelmanager.rs

+7-6
Original file line numberDiff line numberDiff line change
@@ -10120,7 +10120,7 @@ where
1012010120
).map_err(|()| Bolt12SemanticError::InvalidAmount)?;
1012110121

1012210122
let payment_paths = self.create_blinded_payment_paths(
10123-
amount_msat, payment_secret, payment_context, relative_expiry_secs
10123+
amount_msat, payment_secret, payment_context, None, relative_expiry_secs
1012410124
).map_err(|()| Bolt12SemanticError::MissingPaths)?;
1012510125

1012610126
let nonce = Nonce::from_entropy_source(entropy);
@@ -10339,7 +10339,7 @@ where
1033910339
Ok((payment_hash, payment_secret)) => {
1034010340
let payment_context = PaymentContext::Bolt12Refund(Bolt12RefundContext {});
1034110341
let payment_paths = self.create_blinded_payment_paths(
10342-
Some(amount_msats), payment_secret, payment_context, relative_expiry,
10342+
Some(amount_msats), payment_secret, payment_context, None, relative_expiry,
1034310343
)
1034410344
.map_err(|_| Bolt12SemanticError::MissingPaths)?;
1034510345

@@ -10656,7 +10656,7 @@ where
1065610656
/// Creates multi-hop blinded payment paths for the given `amount_msats` by delegating to
1065710657
/// [`Router::create_blinded_payment_paths`].
1065810658
fn create_blinded_payment_paths(
10659-
&self, amount_msats: Option<u64>, payment_secret: PaymentSecret, payment_context: PaymentContext,
10659+
&self, amount_msats: Option<u64>, payment_secret: PaymentSecret, payment_context: PaymentContext, custom_data: Option<Vec<u8>>,
1066010660
relative_expiry_seconds: u32,
1066110661
) -> Result<Vec<BlindedPaymentPath>, ()> {
1066210662
let expanded_key = &self.inbound_payment_key;
@@ -10680,6 +10680,7 @@ where
1068010680
htlc_minimum_msat: 1,
1068110681
},
1068210682
payment_context,
10683+
custom_data,
1068310684
};
1068410685
let nonce = Nonce::from_entropy_source(entropy);
1068510686
let payee_tlvs = payee_tlvs.authenticate(nonce, expanded_key);
@@ -10691,11 +10692,11 @@ where
1069110692

1069210693
#[cfg(all(test, async_payments))]
1069310694
pub(super) fn test_create_blinded_payment_paths(
10694-
&self, amount_msats: Option<u64>, payment_secret: PaymentSecret, payment_context: PaymentContext,
10695+
&self, amount_msats: Option<u64>, payment_secret: PaymentSecret, payment_context: PaymentContext, custom_data: Option<Vec<u8>>,
1069510696
relative_expiry_seconds: u32
1069610697
) -> Result<Vec<BlindedPaymentPath>, ()> {
1069710698
self.create_blinded_payment_paths(
10698-
amount_msats, payment_secret, payment_context, relative_expiry_seconds
10699+
amount_msats, payment_secret, payment_context, custom_data, relative_expiry_seconds
1069910700
)
1070010701
}
1070110702

@@ -12254,7 +12255,7 @@ where
1225412255
invoice_request: invoice_request.fields(),
1225512256
});
1225612257
let payment_paths = match self.create_blinded_payment_paths(
12257-
Some(amount_msats), payment_secret, payment_context, relative_expiry
12258+
Some(amount_msats), payment_secret, payment_context, None, relative_expiry
1225812259
) {
1225912260
Ok(payment_paths) => payment_paths,
1226012261
Err(()) => {

lightning/src/ln/max_payment_path_len_tests.rs

+1
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,7 @@ fn one_hop_blinded_path_with_custom_tlv() {
168168
htlc_minimum_msat: chan_upd_1_2.htlc_minimum_msat,
169169
},
170170
payment_context: PaymentContext::Bolt12Refund(Bolt12RefundContext {}),
171+
custom_data: None,
171172
};
172173
let nonce = Nonce([42u8; 16]);
173174
let expanded_key = chanmon_cfgs[2].keys_manager.get_inbound_payment_key();

lightning/src/ln/msgs.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -2967,6 +2967,7 @@ impl<NS: Deref> ReadableArgs<(Option<PublicKey>, NS)> for InboundOnionPayload wh
29672967
next_blinding_override,
29682968
}))
29692969
},
2970+
// Note: The custom data in the receive tlvs is not used here.
29702971
ChaChaPolyReadAdapter { readable: BlindedPaymentTlvs::Receive(receive_tlvs) } => {
29712972
let ReceiveTlvs { tlvs, authentication: (hmac, nonce) } = receive_tlvs;
29722973
let expanded_key = node_signer.get_inbound_payment_key();
@@ -2975,7 +2976,7 @@ impl<NS: Deref> ReadableArgs<(Option<PublicKey>, NS)> for InboundOnionPayload wh
29752976
}
29762977

29772978
let UnauthenticatedReceiveTlvs {
2978-
payment_secret, payment_constraints, payment_context,
2979+
payment_secret, payment_constraints, payment_context, custom_data
29792980
} = tlvs;
29802981
if total_msat.unwrap_or(0) > MAX_VALUE_MSAT { return Err(DecodeError::InvalidValue) }
29812982
Ok(Self::BlindedReceive(InboundOnionBlindedReceivePayload {
@@ -2989,7 +2990,7 @@ impl<NS: Deref> ReadableArgs<(Option<PublicKey>, NS)> for InboundOnionPayload wh
29892990
keysend_preimage,
29902991
invoice_request,
29912992
sender_custom_tlvs,
2992-
user_custom_data: None,
2993+
user_custom_data: custom_data,
29932994
}))
29942995
},
29952996
}

0 commit comments

Comments
 (0)