Skip to content

Commit

Permalink
Support aggregating htlc_minimum_msat for BlindedPayInfo
Browse files Browse the repository at this point in the history
  • Loading branch information
valentinewallace committed Aug 24, 2023
1 parent 957b17f commit d3a508d
Showing 1 changed file with 113 additions and 1 deletion.
114 changes: 113 additions & 1 deletion lightning/src/blinded_path/payment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -184,11 +184,38 @@ pub(super) fn compute_payinfo(

cltv_expiry_delta = cltv_expiry_delta.checked_add(tlvs.payment_relay.cltv_expiry_delta).ok_or(())?;
}

let mut htlc_minimum_msat = 0;
for (idx, node) in intermediate_nodes.iter().map(|(_, tlvs)| tlvs).enumerate() {
let mut htlc_min_candidate = node.payment_constraints.htlc_minimum_msat as u128;
// Get an iterator over `[curr_hop_tlvs..last_intermediate_hop_tlvs]`.
let next_nodes = intermediate_nodes.iter()
.enumerate()
.skip_while(|(i, _)| *i != idx)
.map(|(_, (_, tlvs))| tlvs);
for node in next_nodes {
// The min htlc for a hop is that hop's htlc_minimum_msat minus the fees paid from that hop to
// the end of the path, because the sender will automatically include that following fee
// amount in the amount that this hop receives.
let prop_fee = node.payment_relay.fee_proportional_millionths as u128;
let base_fee = node.payment_relay.fee_base_msat as u128;
let hop_fee = htlc_min_candidate
.checked_mul(prop_fee)
.and_then(|prop_fee| (prop_fee / 1_000_000).checked_add(base_fee))
.ok_or(())?;
htlc_min_candidate = htlc_min_candidate.saturating_sub(hop_fee);
if htlc_min_candidate == 0 { break }
}
htlc_minimum_msat = core::cmp::max(htlc_min_candidate, htlc_minimum_msat);
}
htlc_minimum_msat =
core::cmp::max(payee_tlvs.payment_constraints.htlc_minimum_msat as u128, htlc_minimum_msat);

Ok(BlindedPayInfo {
fee_base_msat: u32::try_from(curr_base_fee).map_err(|_| ())?,
fee_proportional_millionths: u32::try_from(curr_prop_mil).map_err(|_| ())?,
cltv_expiry_delta,
htlc_minimum_msat: 1, // TODO
htlc_minimum_msat: u64::try_from(htlc_minimum_msat).map_err(|_| ())?,
htlc_maximum_msat: 21_000_000 * 100_000_000 * 1_000, // TODO
features: BlindedHopFeatures::empty(),
})
Expand Down Expand Up @@ -253,6 +280,7 @@ mod tests {
assert_eq!(blinded_payinfo.fee_base_msat, 201);
assert_eq!(blinded_payinfo.fee_proportional_millionths, 1001);
assert_eq!(blinded_payinfo.cltv_expiry_delta, 288);
assert_eq!(blinded_payinfo.htlc_minimum_msat, 900);
}

#[test]
Expand All @@ -268,5 +296,89 @@ mod tests {
assert_eq!(blinded_payinfo.fee_base_msat, 0);
assert_eq!(blinded_payinfo.fee_proportional_millionths, 0);
assert_eq!(blinded_payinfo.cltv_expiry_delta, 0);
assert_eq!(blinded_payinfo.htlc_minimum_msat, 1);
}

#[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![(dummy_pk, 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(),
}), (dummy_pk, 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(),
})];
let recv_tlvs = ReceiveTlvs {
payment_secret: PaymentSecret([0; 32]),
payment_constraints: PaymentConstraints {
max_cltv_expiry: 0,
htlc_minimum_msat: 3,
},
};
let blinded_payinfo = super::compute_payinfo(&intermediate_nodes[..], &recv_tlvs).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![(dummy_pk, 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(),
}), (dummy_pk, 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(),
})];
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).unwrap();
assert_eq!(blinded_payinfo.htlc_minimum_msat, 3797);
}
}

0 comments on commit d3a508d

Please sign in to comment.