lightning_liquidity/lsps2/
utils.rs1use crate::lsps2::msgs::OpeningFeeParams;
4use crate::utils;
5
6use bitcoin::hashes::hmac::{Hmac, HmacEngine};
7use bitcoin::hashes::sha256::Hash as Sha256;
8use bitcoin::hashes::{Hash, HashEngine};
9
10#[cfg(feature = "std")]
11use std::time::{SystemTime, UNIX_EPOCH};
12
13pub fn is_valid_opening_fee_params(
15 fee_params: &OpeningFeeParams, promise_secret: &[u8; 32],
16) -> bool {
17 if is_expired_opening_fee_params(fee_params) {
18 return false;
19 }
20 let mut hmac = HmacEngine::<Sha256>::new(promise_secret);
21 hmac.input(&fee_params.min_fee_msat.to_be_bytes());
22 hmac.input(&fee_params.proportional.to_be_bytes());
23 hmac.input(fee_params.valid_until.to_rfc3339().as_bytes());
24 hmac.input(&fee_params.min_lifetime.to_be_bytes());
25 hmac.input(&fee_params.max_client_to_self_delay.to_be_bytes());
26 hmac.input(&fee_params.min_payment_size_msat.to_be_bytes());
27 hmac.input(&fee_params.max_payment_size_msat.to_be_bytes());
28 let promise_bytes = Hmac::from_engine(hmac).to_byte_array();
29 let promise = utils::hex_str(&promise_bytes[..]);
30 promise == fee_params.promise
31}
32
33#[cfg_attr(not(feature = "std"), allow(unused_variables))]
35pub fn is_expired_opening_fee_params(fee_params: &OpeningFeeParams) -> bool {
36 #[cfg(feature = "std")]
37 {
38 let seconds_since_epoch = SystemTime::now()
39 .duration_since(UNIX_EPOCH)
40 .expect("system clock to be ahead of the unix epoch")
41 .as_secs();
42 let valid_until_seconds_since_epoch = fee_params
43 .valid_until
44 .timestamp()
45 .try_into()
46 .expect("expiration to be ahead of unix epoch");
47 seconds_since_epoch > valid_until_seconds_since_epoch
48 }
49 #[cfg(not(feature = "std"))]
50 {
51 false
53 }
54}
55
56pub fn compute_opening_fee(
62 payment_size_msat: u64, opening_fee_min_fee_msat: u64, opening_fee_proportional: u64,
63) -> Option<u64> {
64 payment_size_msat
65 .checked_mul(opening_fee_proportional)
66 .and_then(|f| f.checked_add(999999))
67 .and_then(|f| f.checked_div(1000000))
68 .map(|f| core::cmp::max(f, opening_fee_min_fee_msat))
69}
70
71#[cfg(test)]
72mod tests {
73 use super::*;
74 use proptest::prelude::*;
75
76 const MAX_VALUE_MSAT: u64 = 21_000_000_0000_0000_000;
77
78 fn arb_opening_fee_params() -> impl Strategy<Value = (u64, u64, u64)> {
79 (0u64..MAX_VALUE_MSAT, 0u64..MAX_VALUE_MSAT, 0u64..MAX_VALUE_MSAT)
80 }
81
82 proptest! {
83 #[test]
84 fn test_compute_opening_fee((payment_size_msat, opening_fee_min_fee_msat, opening_fee_proportional) in arb_opening_fee_params()) {
85 if let Some(res) = compute_opening_fee(payment_size_msat, opening_fee_min_fee_msat, opening_fee_proportional) {
86 assert!(res >= opening_fee_min_fee_msat);
87 assert_eq!(res as f32, (payment_size_msat as f32 * opening_fee_proportional as f32));
88 } else {
89 let max_value = u64::MAX as u128;
91 assert!((payment_size_msat as u128 * opening_fee_proportional as u128) > max_value);
92 }
93 }
94 }
95}