+ assert_eq!(blinded_payinfo.htlc_minimum_msat, 1);
+ assert_eq!(blinded_payinfo.htlc_maximum_msat, 4242);
+ }
+
+ #[test]
+ fn simple_aggregated_htlc_min() {
+ // If no hops charge fees, the htlc_minimum_msat should just be the maximum htlc_minimum_msat
+ // along the path.
+ let dummy_pk = PublicKey::from_slice(&[2; 33]).unwrap();
+ let intermediate_nodes = vec![ForwardNode {
+ node_id: dummy_pk,
+ tlvs: ForwardTlvs {
+ short_channel_id: 0,
+ payment_relay: PaymentRelay {
+ cltv_expiry_delta: 0,
+ fee_proportional_millionths: 0,
+ fee_base_msat: 0,
+ },
+ payment_constraints: PaymentConstraints {
+ max_cltv_expiry: 0,
+ htlc_minimum_msat: 1,
+ },
+ features: BlindedHopFeatures::empty(),
+ },
+ htlc_maximum_msat: u64::max_value()
+ }, ForwardNode {
+ node_id: dummy_pk,
+ tlvs: ForwardTlvs {
+ short_channel_id: 0,
+ payment_relay: PaymentRelay {
+ cltv_expiry_delta: 0,
+ fee_proportional_millionths: 0,
+ fee_base_msat: 0,
+ },
+ payment_constraints: PaymentConstraints {
+ max_cltv_expiry: 0,
+ htlc_minimum_msat: 2_000,
+ },
+ features: BlindedHopFeatures::empty(),
+ },
+ htlc_maximum_msat: u64::max_value()
+ }];
+ let recv_tlvs = ReceiveTlvs {
+ payment_secret: PaymentSecret([0; 32]),
+ payment_constraints: PaymentConstraints {
+ max_cltv_expiry: 0,
+ htlc_minimum_msat: 3,
+ },
+ };
+ let htlc_maximum_msat = 100_000;
+ let blinded_payinfo = super::compute_payinfo(&intermediate_nodes[..], &recv_tlvs, htlc_maximum_msat).unwrap();
+ assert_eq!(blinded_payinfo.htlc_minimum_msat, 2_000);
+ }
+
+ #[test]
+ fn aggregated_htlc_min() {
+ // Create a path with varying fees and htlc_mins, and make sure htlc_minimum_msat ends up as the
+ // max (htlc_min - following_fees) along the path.
+ let dummy_pk = PublicKey::from_slice(&[2; 33]).unwrap();
+ let intermediate_nodes = vec![ForwardNode {
+ node_id: dummy_pk,
+ tlvs: ForwardTlvs {
+ short_channel_id: 0,
+ payment_relay: PaymentRelay {
+ cltv_expiry_delta: 0,
+ fee_proportional_millionths: 500,
+ fee_base_msat: 1_000,
+ },
+ payment_constraints: PaymentConstraints {
+ max_cltv_expiry: 0,
+ htlc_minimum_msat: 5_000,
+ },
+ features: BlindedHopFeatures::empty(),
+ },
+ htlc_maximum_msat: u64::max_value()
+ }, ForwardNode {
+ node_id: dummy_pk,
+ tlvs: ForwardTlvs {
+ short_channel_id: 0,
+ payment_relay: PaymentRelay {
+ cltv_expiry_delta: 0,
+ fee_proportional_millionths: 500,
+ fee_base_msat: 200,
+ },
+ payment_constraints: PaymentConstraints {
+ max_cltv_expiry: 0,
+ htlc_minimum_msat: 2_000,
+ },
+ features: BlindedHopFeatures::empty(),
+ },
+ htlc_maximum_msat: u64::max_value()
+ }];
+ let recv_tlvs = ReceiveTlvs {
+ payment_secret: PaymentSecret([0; 32]),
+ payment_constraints: PaymentConstraints {
+ max_cltv_expiry: 0,
+ htlc_minimum_msat: 1,
+ },
+ };
+ let htlc_minimum_msat = 3798;
+ assert!(super::compute_payinfo(&intermediate_nodes[..], &recv_tlvs, htlc_minimum_msat - 1).is_err());
+
+ let htlc_maximum_msat = htlc_minimum_msat + 1;
+ let blinded_payinfo = super::compute_payinfo(&intermediate_nodes[..], &recv_tlvs, htlc_maximum_msat).unwrap();
+ assert_eq!(blinded_payinfo.htlc_minimum_msat, htlc_minimum_msat);
+ assert_eq!(blinded_payinfo.htlc_maximum_msat, htlc_maximum_msat);
+ }
+
+ #[test]
+ fn aggregated_htlc_max() {
+ // Create a path with varying fees and `htlc_maximum_msat`s, and make sure the aggregated max
+ // htlc ends up as the min (htlc_max - following_fees) along the path.
+ let dummy_pk = PublicKey::from_slice(&[2; 33]).unwrap();
+ let intermediate_nodes = vec![ForwardNode {
+ node_id: dummy_pk,
+ tlvs: ForwardTlvs {
+ short_channel_id: 0,
+ payment_relay: PaymentRelay {
+ cltv_expiry_delta: 0,
+ fee_proportional_millionths: 500,
+ fee_base_msat: 1_000,
+ },
+ payment_constraints: PaymentConstraints {
+ max_cltv_expiry: 0,
+ htlc_minimum_msat: 1,
+ },
+ features: BlindedHopFeatures::empty(),
+ },
+ htlc_maximum_msat: 5_000,
+ }, ForwardNode {
+ node_id: dummy_pk,
+ tlvs: ForwardTlvs {
+ short_channel_id: 0,
+ payment_relay: PaymentRelay {
+ cltv_expiry_delta: 0,
+ fee_proportional_millionths: 500,
+ fee_base_msat: 1,
+ },
+ payment_constraints: PaymentConstraints {
+ max_cltv_expiry: 0,
+ htlc_minimum_msat: 1,
+ },
+ features: BlindedHopFeatures::empty(),
+ },
+ htlc_maximum_msat: 10_000
+ }];
+ let recv_tlvs = ReceiveTlvs {
+ payment_secret: PaymentSecret([0; 32]),
+ payment_constraints: PaymentConstraints {
+ max_cltv_expiry: 0,
+ htlc_minimum_msat: 1,
+ },
+ };
+
+ let blinded_payinfo = super::compute_payinfo(&intermediate_nodes[..], &recv_tlvs, 10_000).unwrap();
+ assert_eq!(blinded_payinfo.htlc_maximum_msat, 3997);