1use bitcoin::hashes::hmac::Hmac;
13use bitcoin::hashes::sha256::Hash as Sha256;
14use bitcoin::secp256k1::{self, PublicKey, Secp256k1, SecretKey};
15
16use crate::blinded_path::{BlindedHop, BlindedPath, IntroductionNode, NodeIdLookUp};
17use crate::blinded_path::utils;
18use crate::crypto::streams::ChaChaPolyReadAdapter;
19use crate::io;
20use crate::io::Cursor;
21use crate::types::payment::PaymentSecret;
22use crate::ln::channel_state::CounterpartyForwardingInfo;
23use crate::ln::channelmanager::Verification;
24use crate::types::features::BlindedHopFeatures;
25use crate::ln::inbound_payment::ExpandedKey;
26use crate::ln::msgs::DecodeError;
27use crate::ln::onion_utils;
28use crate::offers::invoice_request::InvoiceRequestFields;
29use crate::offers::nonce::Nonce;
30use crate::offers::offer::OfferId;
31use crate::routing::gossip::{NodeId, ReadOnlyNetworkGraph};
32use crate::sign::{EntropySource, NodeSigner, Recipient};
33use crate::types::routing::RoutingFees;
34use crate::util::ser::{FixedLengthReader, LengthReadableArgs, HighZeroBytesDroppedBigSize, Readable, WithoutLength, Writeable, Writer};
35
36use core::mem;
37use core::ops::Deref;
38
39#[allow(unused_imports)]
40use crate::prelude::*;
41
42#[derive(Clone, Debug, Hash, Eq, PartialEq)]
44pub struct BlindedPayInfo {
45 pub fee_base_msat: u32,
47
48 pub fee_proportional_millionths: u32,
51
52 pub cltv_expiry_delta: u16,
55
56 pub htlc_minimum_msat: u64,
60
61 pub htlc_maximum_msat: u64,
65
66 pub features: BlindedHopFeatures,
69}
70
71impl_writeable!(BlindedPayInfo, {
72 fee_base_msat,
73 fee_proportional_millionths,
74 cltv_expiry_delta,
75 htlc_minimum_msat,
76 htlc_maximum_msat,
77 features
78});
79
80#[derive(Clone, Debug, Hash, PartialEq, Eq)]
83pub struct BlindedPaymentPath {
84 pub(super) inner_path: BlindedPath,
85 pub payinfo: BlindedPayInfo,
87}
88
89impl BlindedPaymentPath {
90 pub fn one_hop<ES: Deref, T: secp256k1::Signing + secp256k1::Verification>(
92 payee_node_id: PublicKey, payee_tlvs: ReceiveTlvs, min_final_cltv_expiry_delta: u16,
93 entropy_source: ES, secp_ctx: &Secp256k1<T>
94 ) -> Result<Self, ()> where ES::Target: EntropySource {
95 let htlc_maximum_msat = u64::max_value();
98 Self::new(
99 &[], payee_node_id, payee_tlvs, htlc_maximum_msat, min_final_cltv_expiry_delta,
100 entropy_source, secp_ctx
101 )
102 }
103
104 pub fn new<ES: Deref, T: secp256k1::Signing + secp256k1::Verification>(
112 intermediate_nodes: &[PaymentForwardNode], payee_node_id: PublicKey,
113 payee_tlvs: ReceiveTlvs, htlc_maximum_msat: u64, min_final_cltv_expiry_delta: u16,
114 entropy_source: ES, secp_ctx: &Secp256k1<T>,
115 ) -> Result<Self, ()> where ES::Target: EntropySource {
116 let introduction_node = IntroductionNode::NodeId(
117 intermediate_nodes.first().map_or(payee_node_id, |n| n.node_id)
118 );
119 let blinding_secret_bytes = entropy_source.get_secure_random_bytes();
120 let blinding_secret = SecretKey::from_slice(&blinding_secret_bytes[..]).expect("RNG is busted");
121
122 let blinded_payinfo = compute_payinfo(
123 intermediate_nodes, &payee_tlvs.tlvs, htlc_maximum_msat, min_final_cltv_expiry_delta
124 )?;
125 Ok(Self {
126 inner_path: BlindedPath {
127 introduction_node,
128 blinding_point: PublicKey::from_secret_key(secp_ctx, &blinding_secret),
129 blinded_hops: blinded_hops(
130 secp_ctx, intermediate_nodes, payee_node_id, payee_tlvs, &blinding_secret
131 ).map_err(|_| ())?,
132 },
133 payinfo: blinded_payinfo
134 })
135 }
136
137 pub fn public_introduction_node_id<'a>(
140 &self, network_graph: &'a ReadOnlyNetworkGraph
141 ) -> Option<&'a NodeId> {
142 self.inner_path.public_introduction_node_id(network_graph)
143 }
144
145 pub fn introduction_node(&self) -> &IntroductionNode {
147 &self.inner_path.introduction_node
148 }
149
150 pub fn blinding_point(&self) -> PublicKey {
154 self.inner_path.blinding_point
155 }
156
157 pub fn blinded_hops(&self) -> &[BlindedHop] {
159 &self.inner_path.blinded_hops
160 }
161
162 pub fn advance_path_by_one<NS: Deref, NL: Deref, T>(
167 &mut self, node_signer: &NS, node_id_lookup: &NL, secp_ctx: &Secp256k1<T>
168 ) -> Result<(), ()>
169 where
170 NS::Target: NodeSigner,
171 NL::Target: NodeIdLookUp,
172 T: secp256k1::Signing + secp256k1::Verification,
173 {
174 let control_tlvs_ss = node_signer.ecdh(Recipient::Node, &self.inner_path.blinding_point, None)?;
175 let rho = onion_utils::gen_rho_from_shared_secret(&control_tlvs_ss.secret_bytes());
176 let encrypted_control_tlvs = &self.inner_path.blinded_hops.get(0).ok_or(())?.encrypted_payload;
177 let mut s = Cursor::new(encrypted_control_tlvs);
178 let mut reader = FixedLengthReader::new(&mut s, encrypted_control_tlvs.len() as u64);
179 match ChaChaPolyReadAdapter::read(&mut reader, rho) {
180 Ok(ChaChaPolyReadAdapter {
181 readable: BlindedPaymentTlvs::Forward(ForwardTlvs { short_channel_id, .. })
182 }) => {
183 let next_node_id = match node_id_lookup.next_node_id(short_channel_id) {
184 Some(node_id) => node_id,
185 None => return Err(()),
186 };
187 let mut new_blinding_point = onion_utils::next_hop_pubkey(
188 secp_ctx, self.inner_path.blinding_point, control_tlvs_ss.as_ref()
189 ).map_err(|_| ())?;
190 mem::swap(&mut self.inner_path.blinding_point, &mut new_blinding_point);
191 self.inner_path.introduction_node = IntroductionNode::NodeId(next_node_id);
192 self.inner_path.blinded_hops.remove(0);
193 Ok(())
194 },
195 _ => Err(())
196 }
197 }
198
199 pub(crate) fn inner_blinded_path(&self) -> &BlindedPath {
200 &self.inner_path
201 }
202
203 pub(crate) fn from_parts(inner_path: BlindedPath, payinfo: BlindedPayInfo) -> Self {
204 Self { inner_path, payinfo }
205 }
206
207 #[cfg(any(test, fuzzing))]
208 pub fn from_raw(
209 introduction_node_id: PublicKey, blinding_point: PublicKey, blinded_hops: Vec<BlindedHop>,
210 payinfo: BlindedPayInfo
211 ) -> Self {
212 Self {
213 inner_path: BlindedPath {
214 introduction_node: IntroductionNode::NodeId(introduction_node_id),
215 blinding_point,
216 blinded_hops,
217 },
218 payinfo
219 }
220 }
221
222 #[cfg(test)]
223 pub fn clear_blinded_hops(&mut self) {
224 self.inner_path.blinded_hops.clear()
225 }
226}
227
228#[derive(Clone, Debug)]
230pub struct PaymentForwardNode {
231 pub tlvs: ForwardTlvs,
234 pub node_id: PublicKey,
236 pub htlc_maximum_msat: u64,
238}
239
240#[derive(Clone, Debug)]
242pub struct ForwardTlvs {
243 pub short_channel_id: u64,
245 pub payment_relay: PaymentRelay,
247 pub payment_constraints: PaymentConstraints,
249 pub features: BlindedHopFeatures,
254 pub next_blinding_override: Option<PublicKey>,
257}
258
259#[derive(Clone, Debug)]
264pub struct ReceiveTlvs {
265 pub(crate) tlvs: UnauthenticatedReceiveTlvs,
267 pub(crate) authentication: (Hmac<Sha256>, Nonce),
269}
270
271impl ReceiveTlvs {
272 pub fn tlvs(&self) -> &UnauthenticatedReceiveTlvs {
274 &self.tlvs
275 }
276}
277
278#[derive(Clone, Debug)]
280pub struct UnauthenticatedReceiveTlvs {
281 pub payment_secret: PaymentSecret,
283 pub payment_constraints: PaymentConstraints,
285 pub payment_context: PaymentContext,
287}
288
289impl UnauthenticatedReceiveTlvs {
290 pub fn authenticate(self, nonce: Nonce, expanded_key: &ExpandedKey) -> ReceiveTlvs {
293 ReceiveTlvs {
294 authentication: (self.hmac_for_offer_payment(nonce, expanded_key), nonce),
295 tlvs: self,
296 }
297 }
298}
299
300pub(crate) enum BlindedPaymentTlvs {
304 Forward(ForwardTlvs),
306 Receive(ReceiveTlvs),
308}
309
310enum BlindedPaymentTlvsRef<'a> {
312 Forward(&'a ForwardTlvs),
313 Receive(&'a ReceiveTlvs),
314}
315
316#[derive(Clone, Debug, PartialEq)]
320pub struct PaymentRelay {
321 pub cltv_expiry_delta: u16,
323 pub fee_proportional_millionths: u32,
326 pub fee_base_msat: u32,
328}
329
330#[derive(Clone, Debug, PartialEq)]
334pub struct PaymentConstraints {
335 pub max_cltv_expiry: u32,
337 pub htlc_minimum_msat: u64,
340}
341
342#[derive(Clone, Debug, Eq, PartialEq)]
347pub enum PaymentContext {
348 Bolt12Offer(Bolt12OfferContext),
352
353 Bolt12Refund(Bolt12RefundContext),
357}
358
359pub(crate) enum PaymentContextRef<'a> {
361 Bolt12Offer(&'a Bolt12OfferContext),
362 Bolt12Refund(&'a Bolt12RefundContext),
363}
364
365#[derive(Clone, Debug, Eq, PartialEq)]
369pub struct Bolt12OfferContext {
370 pub offer_id: OfferId,
374
375 pub invoice_request: InvoiceRequestFields,
380}
381
382#[derive(Clone, Debug, Eq, PartialEq)]
386pub struct Bolt12RefundContext {}
387
388impl TryFrom<CounterpartyForwardingInfo> for PaymentRelay {
389 type Error = ();
390
391 fn try_from(info: CounterpartyForwardingInfo) -> Result<Self, ()> {
392 let CounterpartyForwardingInfo {
393 fee_base_msat, fee_proportional_millionths, cltv_expiry_delta
394 } = info;
395
396 let cltv_expiry_delta = match cltv_expiry_delta {
398 0..=40 => 40,
399 41..=80 => 80,
400 81..=144 => 144,
401 145..=216 => 216,
402 _ => return Err(()),
403 };
404
405 Ok(Self { cltv_expiry_delta, fee_proportional_millionths, fee_base_msat })
406 }
407}
408
409impl Writeable for ForwardTlvs {
410 fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
411 let features_opt =
412 if self.features == BlindedHopFeatures::empty() { None }
413 else { Some(WithoutLength(&self.features)) };
414 encode_tlv_stream!(w, {
415 (2, self.short_channel_id, required),
416 (10, self.payment_relay, required),
417 (12, self.payment_constraints, required),
418 (14, features_opt, option)
419 });
420 Ok(())
421 }
422}
423
424impl Writeable for ReceiveTlvs {
425 fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
426 encode_tlv_stream!(w, {
427 (12, self.tlvs.payment_constraints, required),
428 (65536, self.tlvs.payment_secret, required),
429 (65537, self.tlvs.payment_context, required),
430 (65539, self.authentication, required),
431 });
432 Ok(())
433 }
434}
435
436impl Writeable for UnauthenticatedReceiveTlvs {
437 fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
438 encode_tlv_stream!(w, {
439 (12, self.payment_constraints, required),
440 (65536, self.payment_secret, required),
441 (65537, self.payment_context, required),
442 });
443 Ok(())
444 }
445}
446
447impl<'a> Writeable for BlindedPaymentTlvsRef<'a> {
448 fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
449 match self {
451 Self::Forward(tlvs) => tlvs.write(w)?,
452 Self::Receive(tlvs) => tlvs.write(w)?,
453 }
454 Ok(())
455 }
456}
457
458impl Readable for BlindedPaymentTlvs {
459 fn read<R: io::Read>(r: &mut R) -> Result<Self, DecodeError> {
460 _init_and_read_tlv_stream!(r, {
461 (1, _padding, option),
462 (2, scid, option),
463 (8, next_blinding_override, option),
464 (10, payment_relay, option),
465 (12, payment_constraints, required),
466 (14, features, (option, encoding: (BlindedHopFeatures, WithoutLength))),
467 (65536, payment_secret, option),
468 (65537, payment_context, option),
469 (65539, authentication, option),
470 });
471 let _padding: Option<utils::Padding> = _padding;
472
473 if let Some(short_channel_id) = scid {
474 if payment_secret.is_some() {
475 return Err(DecodeError::InvalidValue)
476 }
477 Ok(BlindedPaymentTlvs::Forward(ForwardTlvs {
478 short_channel_id,
479 payment_relay: payment_relay.ok_or(DecodeError::InvalidValue)?,
480 payment_constraints: payment_constraints.0.unwrap(),
481 next_blinding_override,
482 features: features.unwrap_or_else(BlindedHopFeatures::empty),
483 }))
484 } else {
485 if payment_relay.is_some() || features.is_some() { return Err(DecodeError::InvalidValue) }
486 Ok(BlindedPaymentTlvs::Receive(ReceiveTlvs {
487 tlvs: UnauthenticatedReceiveTlvs {
488 payment_secret: payment_secret.ok_or(DecodeError::InvalidValue)?,
489 payment_constraints: payment_constraints.0.unwrap(),
490 payment_context: payment_context.ok_or(DecodeError::InvalidValue)?,
491 },
492 authentication: authentication.ok_or(DecodeError::InvalidValue)?,
493 }))
494 }
495 }
496}
497
498pub(super) fn blinded_hops<T: secp256k1::Signing + secp256k1::Verification>(
500 secp_ctx: &Secp256k1<T>, intermediate_nodes: &[PaymentForwardNode],
501 payee_node_id: PublicKey, payee_tlvs: ReceiveTlvs, session_priv: &SecretKey,
502) -> Result<Vec<BlindedHop>, secp256k1::Error> {
503 let pks = intermediate_nodes.iter().map(|node| node.node_id)
504 .chain(core::iter::once(payee_node_id));
505 let tlvs = intermediate_nodes.iter().map(|node| BlindedPaymentTlvsRef::Forward(&node.tlvs))
506 .chain(core::iter::once(BlindedPaymentTlvsRef::Receive(&payee_tlvs)));
507
508 let path = pks.zip(tlvs);
509
510 utils::construct_blinded_hops(secp_ctx, path, session_priv)
511}
512
513pub(crate) fn amt_to_forward_msat(inbound_amt_msat: u64, payment_relay: &PaymentRelay) -> Option<u64> {
515 let inbound_amt = inbound_amt_msat as u128;
516 let base = payment_relay.fee_base_msat as u128;
517 let prop = payment_relay.fee_proportional_millionths as u128;
518
519 let post_base_fee_inbound_amt =
520 if let Some(amt) = inbound_amt.checked_sub(base) { amt } else { return None };
521 let mut amt_to_forward =
522 (post_base_fee_inbound_amt * 1_000_000 + 1_000_000 + prop - 1) / (prop + 1_000_000);
523
524 let fee = ((amt_to_forward * prop) / 1_000_000) + base;
525 if inbound_amt - fee < amt_to_forward {
526 amt_to_forward -= 1;
529 }
530 debug_assert_eq!(amt_to_forward + fee, inbound_amt);
531 u64::try_from(amt_to_forward).ok()
532}
533
534pub(crate) fn compute_aggregated_base_prop_fee<I>(hops_fees: I) -> Result<(u64, u64), ()>
536where
537 I: DoubleEndedIterator<Item = RoutingFees>,
538{
539 let mut curr_base_fee: u64 = 0;
540 let mut curr_prop_mil: u64 = 0;
541 for fees in hops_fees.rev() {
542 let next_base_fee = fees.base_msat as u64;
543 let next_prop_mil = fees.proportional_millionths as u64;
544
545 curr_base_fee = curr_base_fee.checked_mul(1_000_000 + next_prop_mil)
548 .and_then(|f| f.checked_add(1_000_000 - 1))
549 .map(|f| f / 1_000_000)
550 .and_then(|f| f.checked_add(next_base_fee))
551 .ok_or(())?;
552 curr_prop_mil = curr_prop_mil.checked_add(1_000_000)
554 .and_then(|f1| next_prop_mil.checked_add(1_000_000).and_then(|f2| f2.checked_mul(f1)))
555 .and_then(|f| f.checked_add(1_000_000 - 1))
556 .map(|f| f / 1_000_000)
557 .and_then(|f| f.checked_sub(1_000_000))
558 .ok_or(())?;
559 }
560
561 Ok((curr_base_fee, curr_prop_mil))
562}
563
564pub(super) fn compute_payinfo(
565 intermediate_nodes: &[PaymentForwardNode], payee_tlvs: &UnauthenticatedReceiveTlvs,
566 payee_htlc_maximum_msat: u64, min_final_cltv_expiry_delta: u16,
567) -> Result<BlindedPayInfo, ()> {
568 let (aggregated_base_fee, aggregated_prop_fee) =
569 compute_aggregated_base_prop_fee(intermediate_nodes.iter().map(|node| RoutingFees {
570 base_msat: node.tlvs.payment_relay.fee_base_msat,
571 proportional_millionths: node.tlvs.payment_relay.fee_proportional_millionths,
572 }))?;
573
574 let mut htlc_minimum_msat: u64 = 1;
575 let mut htlc_maximum_msat: u64 = 21_000_000 * 100_000_000 * 1_000; let mut cltv_expiry_delta: u16 = min_final_cltv_expiry_delta;
577 for node in intermediate_nodes.iter() {
578 if node.tlvs.features.requires_unknown_bits_from(&BlindedHopFeatures::empty()) {
581 return Err(());
582 }
583
584 cltv_expiry_delta =
585 cltv_expiry_delta.checked_add(node.tlvs.payment_relay.cltv_expiry_delta).ok_or(())?;
586
587 htlc_minimum_msat = amt_to_forward_msat(
591 core::cmp::max(node.tlvs.payment_constraints.htlc_minimum_msat, htlc_minimum_msat),
592 &node.tlvs.payment_relay
593 ).unwrap_or(1); htlc_maximum_msat = amt_to_forward_msat(
595 core::cmp::min(node.htlc_maximum_msat, htlc_maximum_msat), &node.tlvs.payment_relay
596 ).ok_or(())?; }
598 htlc_minimum_msat = core::cmp::max(
599 payee_tlvs.payment_constraints.htlc_minimum_msat, htlc_minimum_msat
600 );
601 htlc_maximum_msat = core::cmp::min(payee_htlc_maximum_msat, htlc_maximum_msat);
602
603 if htlc_maximum_msat < htlc_minimum_msat { return Err(()) }
604 Ok(BlindedPayInfo {
605 fee_base_msat: u32::try_from(aggregated_base_fee).map_err(|_| ())?,
606 fee_proportional_millionths: u32::try_from(aggregated_prop_fee).map_err(|_| ())?,
607 cltv_expiry_delta,
608 htlc_minimum_msat,
609 htlc_maximum_msat,
610 features: BlindedHopFeatures::empty(),
611 })
612}
613
614impl Writeable for PaymentRelay {
615 fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
616 self.cltv_expiry_delta.write(w)?;
617 self.fee_proportional_millionths.write(w)?;
618 HighZeroBytesDroppedBigSize(self.fee_base_msat).write(w)
619 }
620}
621impl Readable for PaymentRelay {
622 fn read<R: io::Read>(r: &mut R) -> Result<Self, DecodeError> {
623 let cltv_expiry_delta: u16 = Readable::read(r)?;
624 let fee_proportional_millionths: u32 = Readable::read(r)?;
625 let fee_base_msat: HighZeroBytesDroppedBigSize<u32> = Readable::read(r)?;
626 Ok(Self { cltv_expiry_delta, fee_proportional_millionths, fee_base_msat: fee_base_msat.0 })
627 }
628}
629
630impl Writeable for PaymentConstraints {
631 fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
632 self.max_cltv_expiry.write(w)?;
633 HighZeroBytesDroppedBigSize(self.htlc_minimum_msat).write(w)
634 }
635}
636impl Readable for PaymentConstraints {
637 fn read<R: io::Read>(r: &mut R) -> Result<Self, DecodeError> {
638 let max_cltv_expiry: u32 = Readable::read(r)?;
639 let htlc_minimum_msat: HighZeroBytesDroppedBigSize<u64> = Readable::read(r)?;
640 Ok(Self { max_cltv_expiry, htlc_minimum_msat: htlc_minimum_msat.0 })
641 }
642}
643
644impl_writeable_tlv_based_enum_legacy!(PaymentContext,
645 ;
646 (1, Bolt12Offer),
648 (2, Bolt12Refund),
649);
650
651impl<'a> Writeable for PaymentContextRef<'a> {
652 fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
653 match self {
654 PaymentContextRef::Bolt12Offer(context) => {
655 1u8.write(w)?;
656 context.write(w)?;
657 },
658 PaymentContextRef::Bolt12Refund(context) => {
659 2u8.write(w)?;
660 context.write(w)?;
661 },
662 }
663
664 Ok(())
665 }
666}
667
668impl_writeable_tlv_based!(Bolt12OfferContext, {
669 (0, offer_id, required),
670 (2, invoice_request, required),
671});
672
673impl_writeable_tlv_based!(Bolt12RefundContext, {});
674
675#[cfg(test)]
676mod tests {
677 use bitcoin::secp256k1::PublicKey;
678 use crate::blinded_path::payment::{Bolt12RefundContext, PaymentForwardNode, ForwardTlvs, PaymentConstraints, PaymentContext, PaymentRelay, UnauthenticatedReceiveTlvs};
679 use crate::types::payment::PaymentSecret;
680 use crate::types::features::BlindedHopFeatures;
681 use crate::ln::functional_test_utils::TEST_FINAL_CLTV;
682
683 #[test]
684 fn compute_payinfo() {
685 let dummy_pk = PublicKey::from_slice(&[2; 33]).unwrap();
688 let intermediate_nodes = vec![PaymentForwardNode {
689 node_id: dummy_pk,
690 tlvs: ForwardTlvs {
691 short_channel_id: 0,
692 payment_relay: PaymentRelay {
693 cltv_expiry_delta: 144,
694 fee_proportional_millionths: 500,
695 fee_base_msat: 100,
696 },
697 payment_constraints: PaymentConstraints {
698 max_cltv_expiry: 0,
699 htlc_minimum_msat: 100,
700 },
701 next_blinding_override: None,
702 features: BlindedHopFeatures::empty(),
703 },
704 htlc_maximum_msat: u64::max_value(),
705 }, PaymentForwardNode {
706 node_id: dummy_pk,
707 tlvs: ForwardTlvs {
708 short_channel_id: 0,
709 payment_relay: PaymentRelay {
710 cltv_expiry_delta: 144,
711 fee_proportional_millionths: 500,
712 fee_base_msat: 100,
713 },
714 payment_constraints: PaymentConstraints {
715 max_cltv_expiry: 0,
716 htlc_minimum_msat: 1_000,
717 },
718 next_blinding_override: None,
719 features: BlindedHopFeatures::empty(),
720 },
721 htlc_maximum_msat: u64::max_value(),
722 }];
723 let recv_tlvs = UnauthenticatedReceiveTlvs {
724 payment_secret: PaymentSecret([0; 32]),
725 payment_constraints: PaymentConstraints {
726 max_cltv_expiry: 0,
727 htlc_minimum_msat: 1,
728 },
729 payment_context: PaymentContext::Bolt12Refund(Bolt12RefundContext {}),
730 };
731 let htlc_maximum_msat = 100_000;
732 let blinded_payinfo = super::compute_payinfo(&intermediate_nodes[..], &recv_tlvs, htlc_maximum_msat, 12).unwrap();
733 assert_eq!(blinded_payinfo.fee_base_msat, 201);
734 assert_eq!(blinded_payinfo.fee_proportional_millionths, 1001);
735 assert_eq!(blinded_payinfo.cltv_expiry_delta, 300);
736 assert_eq!(blinded_payinfo.htlc_minimum_msat, 900);
737 assert_eq!(blinded_payinfo.htlc_maximum_msat, htlc_maximum_msat);
738 }
739
740 #[test]
741 fn compute_payinfo_1_hop() {
742 let recv_tlvs = UnauthenticatedReceiveTlvs {
743 payment_secret: PaymentSecret([0; 32]),
744 payment_constraints: PaymentConstraints {
745 max_cltv_expiry: 0,
746 htlc_minimum_msat: 1,
747 },
748 payment_context: PaymentContext::Bolt12Refund(Bolt12RefundContext {}),
749 };
750 let blinded_payinfo = super::compute_payinfo(&[], &recv_tlvs, 4242, TEST_FINAL_CLTV as u16).unwrap();
751 assert_eq!(blinded_payinfo.fee_base_msat, 0);
752 assert_eq!(blinded_payinfo.fee_proportional_millionths, 0);
753 assert_eq!(blinded_payinfo.cltv_expiry_delta, TEST_FINAL_CLTV as u16);
754 assert_eq!(blinded_payinfo.htlc_minimum_msat, 1);
755 assert_eq!(blinded_payinfo.htlc_maximum_msat, 4242);
756 }
757
758 #[test]
759 fn simple_aggregated_htlc_min() {
760 let dummy_pk = PublicKey::from_slice(&[2; 33]).unwrap();
763 let intermediate_nodes = vec![PaymentForwardNode {
764 node_id: dummy_pk,
765 tlvs: ForwardTlvs {
766 short_channel_id: 0,
767 payment_relay: PaymentRelay {
768 cltv_expiry_delta: 0,
769 fee_proportional_millionths: 0,
770 fee_base_msat: 0,
771 },
772 payment_constraints: PaymentConstraints {
773 max_cltv_expiry: 0,
774 htlc_minimum_msat: 1,
775 },
776 next_blinding_override: None,
777 features: BlindedHopFeatures::empty(),
778 },
779 htlc_maximum_msat: u64::max_value()
780 }, PaymentForwardNode {
781 node_id: dummy_pk,
782 tlvs: ForwardTlvs {
783 short_channel_id: 0,
784 payment_relay: PaymentRelay {
785 cltv_expiry_delta: 0,
786 fee_proportional_millionths: 0,
787 fee_base_msat: 0,
788 },
789 payment_constraints: PaymentConstraints {
790 max_cltv_expiry: 0,
791 htlc_minimum_msat: 2_000,
792 },
793 next_blinding_override: None,
794 features: BlindedHopFeatures::empty(),
795 },
796 htlc_maximum_msat: u64::max_value()
797 }];
798 let recv_tlvs = UnauthenticatedReceiveTlvs {
799 payment_secret: PaymentSecret([0; 32]),
800 payment_constraints: PaymentConstraints {
801 max_cltv_expiry: 0,
802 htlc_minimum_msat: 3,
803 },
804 payment_context: PaymentContext::Bolt12Refund(Bolt12RefundContext {}),
805 };
806 let htlc_maximum_msat = 100_000;
807 let blinded_payinfo = super::compute_payinfo(&intermediate_nodes[..], &recv_tlvs, htlc_maximum_msat, TEST_FINAL_CLTV as u16).unwrap();
808 assert_eq!(blinded_payinfo.htlc_minimum_msat, 2_000);
809 }
810
811 #[test]
812 fn aggregated_htlc_min() {
813 let dummy_pk = PublicKey::from_slice(&[2; 33]).unwrap();
816 let intermediate_nodes = vec![PaymentForwardNode {
817 node_id: dummy_pk,
818 tlvs: ForwardTlvs {
819 short_channel_id: 0,
820 payment_relay: PaymentRelay {
821 cltv_expiry_delta: 0,
822 fee_proportional_millionths: 500,
823 fee_base_msat: 1_000,
824 },
825 payment_constraints: PaymentConstraints {
826 max_cltv_expiry: 0,
827 htlc_minimum_msat: 5_000,
828 },
829 next_blinding_override: None,
830 features: BlindedHopFeatures::empty(),
831 },
832 htlc_maximum_msat: u64::max_value()
833 }, PaymentForwardNode {
834 node_id: dummy_pk,
835 tlvs: ForwardTlvs {
836 short_channel_id: 0,
837 payment_relay: PaymentRelay {
838 cltv_expiry_delta: 0,
839 fee_proportional_millionths: 500,
840 fee_base_msat: 200,
841 },
842 payment_constraints: PaymentConstraints {
843 max_cltv_expiry: 0,
844 htlc_minimum_msat: 2_000,
845 },
846 next_blinding_override: None,
847 features: BlindedHopFeatures::empty(),
848 },
849 htlc_maximum_msat: u64::max_value()
850 }];
851 let recv_tlvs = UnauthenticatedReceiveTlvs {
852 payment_secret: PaymentSecret([0; 32]),
853 payment_constraints: PaymentConstraints {
854 max_cltv_expiry: 0,
855 htlc_minimum_msat: 1,
856 },
857 payment_context: PaymentContext::Bolt12Refund(Bolt12RefundContext {}),
858 };
859 let htlc_minimum_msat = 3798;
860 assert!(super::compute_payinfo(&intermediate_nodes[..], &recv_tlvs, htlc_minimum_msat - 1, TEST_FINAL_CLTV as u16).is_err());
861
862 let htlc_maximum_msat = htlc_minimum_msat + 1;
863 let blinded_payinfo = super::compute_payinfo(&intermediate_nodes[..], &recv_tlvs, htlc_maximum_msat, TEST_FINAL_CLTV as u16).unwrap();
864 assert_eq!(blinded_payinfo.htlc_minimum_msat, htlc_minimum_msat);
865 assert_eq!(blinded_payinfo.htlc_maximum_msat, htlc_maximum_msat);
866 }
867
868 #[test]
869 fn aggregated_htlc_max() {
870 let dummy_pk = PublicKey::from_slice(&[2; 33]).unwrap();
873 let intermediate_nodes = vec![PaymentForwardNode {
874 node_id: dummy_pk,
875 tlvs: ForwardTlvs {
876 short_channel_id: 0,
877 payment_relay: PaymentRelay {
878 cltv_expiry_delta: 0,
879 fee_proportional_millionths: 500,
880 fee_base_msat: 1_000,
881 },
882 payment_constraints: PaymentConstraints {
883 max_cltv_expiry: 0,
884 htlc_minimum_msat: 1,
885 },
886 next_blinding_override: None,
887 features: BlindedHopFeatures::empty(),
888 },
889 htlc_maximum_msat: 5_000,
890 }, PaymentForwardNode {
891 node_id: dummy_pk,
892 tlvs: ForwardTlvs {
893 short_channel_id: 0,
894 payment_relay: PaymentRelay {
895 cltv_expiry_delta: 0,
896 fee_proportional_millionths: 500,
897 fee_base_msat: 1,
898 },
899 payment_constraints: PaymentConstraints {
900 max_cltv_expiry: 0,
901 htlc_minimum_msat: 1,
902 },
903 next_blinding_override: None,
904 features: BlindedHopFeatures::empty(),
905 },
906 htlc_maximum_msat: 10_000
907 }];
908 let recv_tlvs = UnauthenticatedReceiveTlvs {
909 payment_secret: PaymentSecret([0; 32]),
910 payment_constraints: PaymentConstraints {
911 max_cltv_expiry: 0,
912 htlc_minimum_msat: 1,
913 },
914 payment_context: PaymentContext::Bolt12Refund(Bolt12RefundContext {}),
915 };
916
917 let blinded_payinfo = super::compute_payinfo(&intermediate_nodes[..], &recv_tlvs, 10_000, TEST_FINAL_CLTV as u16).unwrap();
918 assert_eq!(blinded_payinfo.htlc_maximum_msat, 3997);
919 }
920}