Skip to content

Commit bd358f3

Browse files
committed
Drop proptests in lightning-liquidity
`proptest`'s transitive dependency tree has always been somewhat large, but one of them (`rusty-fork`'s `tempfile` dependency) just went ahead with a bump of their `rand` dependency, breaking our MSRV yet again. Because we don't actually use `proptest` for anything interesting, the simplest solution is to simply drop it, which we do here. Note that we'll likely transition the LSPS5 URL type to simply use the `bitreq` URL type over the next few days anyway, so there's not much reason to care about its continued test coverage. Further, in writing this commit it was discovered that our tests in `lsps2/utils.rs` were actually broken on the vast majority of inputs, but proptest wasn't testing with any interesting test cases at all, causing it to be missed entirely!
1 parent 927edf1 commit bd358f3

5 files changed

Lines changed: 45 additions & 217 deletions

File tree

ci/ci-tests.sh

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,6 @@ PIN_RELEASE_DEPS # pin the release dependencies in our main workspace
1616
# The backtrace v0.3.75 crate relies on rustc 1.82
1717
[ "$RUSTC_MINOR_VERSION" -lt 82 ] && cargo update -p backtrace --precise "0.3.74" --quiet
1818

19-
# proptest 1.9.0 requires rustc 1.82.0
20-
[ "$RUSTC_MINOR_VERSION" -lt 82 ] && cargo update -p proptest --precise "1.8.0" --quiet
21-
2219
# Starting with version 1.2.0, the `idna_adapter` crate has an MSRV of rustc 1.81.0.
2320
[ "$RUSTC_MINOR_VERSION" -lt 81 ] && cargo update -p idna_adapter --precise "1.1.0" --quiet
2421

lightning-liquidity/Cargo.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@ lightning = { version = "0.3.0", path = "../lightning", default-features = false
3939
lightning-invoice = { version = "0.35.0", path = "../lightning-invoice", default-features = false, features = ["serde", "std"] }
4040
lightning-persister = { version = "0.3.0", path = "../lightning-persister", default-features = false }
4141

42-
proptest = "1.0.0"
4342
tokio = { version = "1.35", default-features = false, features = [ "rt-multi-thread", "time", "sync", "macros" ] }
4443
parking_lot = { version = "0.12", default-features = false }
4544

lightning-liquidity/src/lsps2/service.rs

Lines changed: 44 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -2351,64 +2351,58 @@ mod tests {
23512351

23522352
use crate::lsps0::ser::LSPSDateTime;
23532353

2354-
use proptest::prelude::*;
2355-
23562354
use bitcoin::{absolute::LockTime, transaction::Version};
23572355
use core::str::FromStr;
23582356

23592357
const MAX_VALUE_MSAT: u64 = 21_000_000_0000_0000_000;
23602358

2361-
fn arb_forward_amounts() -> impl Strategy<Value = (u64, u64, u64, u64)> {
2362-
(1u64..MAX_VALUE_MSAT, 1u64..MAX_VALUE_MSAT, 1u64..MAX_VALUE_MSAT, 1u64..MAX_VALUE_MSAT)
2363-
.prop_map(|(a, b, c, d)| {
2364-
(a, b, c, core::cmp::min(d, a.saturating_add(b).saturating_add(c)))
2365-
})
2366-
}
2359+
#[test]
2360+
fn rand_test_calculate_amount_to_forward() {
2361+
use std::collections::hash_map::RandomState;
2362+
use std::hash::{BuildHasher, Hasher};
2363+
2364+
let total_fee_msat = RandomState::new().build_hasher().finish() % MAX_VALUE_MSAT;
2365+
let htlc_count = (RandomState::new().build_hasher().finish() % 10) as u8;
2366+
2367+
let mut htlcs = Vec::new();
2368+
let mut total_received_msat = 0;
2369+
let mut htlc_values = Vec::new();
2370+
for i in 0..htlc_count {
2371+
let expected_outbound_amount_msat =
2372+
RandomState::new().build_hasher().finish() % MAX_VALUE_MSAT;
2373+
if total_received_msat + expected_outbound_amount_msat > MAX_VALUE_MSAT {
2374+
break;
2375+
}
2376+
total_received_msat += expected_outbound_amount_msat;
2377+
htlc_values.push(total_received_msat);
2378+
htlcs.push(InterceptedHTLC {
2379+
intercept_id: InterceptId([i; 32]),
2380+
expected_outbound_amount_msat,
2381+
payment_hash: PaymentHash([i; 32]),
2382+
});
2383+
}
23672384

2368-
proptest! {
2369-
#[test]
2370-
fn proptest_calculate_amount_to_forward((o_0, o_1, o_2, total_fee_msat) in arb_forward_amounts()) {
2371-
let htlcs = vec![
2372-
InterceptedHTLC {
2373-
intercept_id: InterceptId([0; 32]),
2374-
expected_outbound_amount_msat: o_0,
2375-
payment_hash: PaymentHash([0; 32]),
2376-
},
2377-
InterceptedHTLC {
2378-
intercept_id: InterceptId([1; 32]),
2379-
expected_outbound_amount_msat: o_1,
2380-
payment_hash: PaymentHash([0; 32]),
2381-
},
2382-
InterceptedHTLC {
2383-
intercept_id: InterceptId([2; 32]),
2384-
expected_outbound_amount_msat: o_2,
2385-
payment_hash: PaymentHash([0; 32]),
2386-
},
2387-
];
2385+
if total_fee_msat > total_received_msat {
2386+
return;
2387+
}
23882388

2389-
let result = calculate_amount_to_forward_per_htlc(&htlcs, total_fee_msat);
2390-
let total_received_msat = o_0 + o_1 + o_2;
2389+
let result = calculate_amount_to_forward_per_htlc(&htlcs, total_fee_msat);
23912390

2392-
if total_received_msat < total_fee_msat {
2393-
assert_eq!(result.len(), 0);
2394-
} else {
2395-
assert_ne!(result.len(), 0);
2396-
assert_eq!(result[0].0, htlcs[0].intercept_id);
2397-
assert_eq!(result[1].0, htlcs[1].intercept_id);
2398-
assert_eq!(result[2].0, htlcs[2].intercept_id);
2399-
assert!(result[0].1 <= o_0);
2400-
assert!(result[1].1 <= o_1);
2401-
assert!(result[2].1 <= o_2);
2402-
2403-
let result_sum = result.iter().map(|(_, f)| f).sum::<u64>();
2404-
assert_eq!(total_received_msat - result_sum, total_fee_msat);
2405-
let five_pct = result_sum as f32 * 0.05;
2406-
let fair_share_0 = (o_0 as f32 / total_received_msat as f32) * result_sum as f32;
2407-
assert!(result[0].1 as f32 <= fair_share_0 + five_pct);
2408-
let fair_share_1 = (o_1 as f32 / total_received_msat as f32) * result_sum as f32;
2409-
assert!(result[1].1 as f32 <= fair_share_1 + five_pct);
2410-
let fair_share_2 = (o_2 as f32 / total_received_msat as f32) * result_sum as f32;
2411-
assert!(result[2].1 as f32 <= fair_share_2 + five_pct);
2391+
if total_received_msat < total_fee_msat {
2392+
assert_eq!(result.len(), 0);
2393+
} else {
2394+
assert_eq!(result.len(), htlcs.len());
2395+
let result_sum = result.iter().map(|(_, f)| f).sum::<u64>();
2396+
assert_eq!(total_received_msat - result_sum, total_fee_msat);
2397+
let five_pct = result_sum as f32 * 0.05;
2398+
2399+
for ((htlc, htlc_value), res) in htlcs.iter().zip(htlc_values).zip(result.iter()) {
2400+
assert_eq!(res.0, htlc.intercept_id);
2401+
assert!(res.1 <= htlc_value);
2402+
2403+
let fair_share =
2404+
(htlc_value as f32 / total_received_msat as f32) * result_sum as f32;
2405+
assert!(res.1 as f32 <= fair_share + five_pct);
24122406
}
24132407
}
24142408
}

lightning-liquidity/src/lsps2/utils.rs

Lines changed: 1 addition & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -60,33 +60,6 @@ pub fn compute_opening_fee(
6060
) -> Option<u64> {
6161
payment_size_msat
6262
.checked_mul(opening_fee_proportional)
63-
.and_then(|f| f.checked_add(999999))
64-
.and_then(|f| f.checked_div(1000000))
63+
.map(|f| f.div_ceil(1_000_000))
6564
.map(|f| core::cmp::max(f, opening_fee_min_fee_msat))
6665
}
67-
68-
#[cfg(test)]
69-
mod tests {
70-
use super::*;
71-
use proptest::prelude::*;
72-
73-
const MAX_VALUE_MSAT: u64 = 21_000_000_0000_0000_000;
74-
75-
fn arb_opening_fee_params() -> impl Strategy<Value = (u64, u64, u64)> {
76-
(0u64..MAX_VALUE_MSAT, 0u64..MAX_VALUE_MSAT, 0u64..MAX_VALUE_MSAT)
77-
}
78-
79-
proptest! {
80-
#[test]
81-
fn test_compute_opening_fee((payment_size_msat, opening_fee_min_fee_msat, opening_fee_proportional) in arb_opening_fee_params()) {
82-
if let Some(res) = compute_opening_fee(payment_size_msat, opening_fee_min_fee_msat, opening_fee_proportional) {
83-
assert!(res >= opening_fee_min_fee_msat);
84-
assert_eq!(res as f32, (payment_size_msat as f32 * opening_fee_proportional as f32));
85-
} else {
86-
// Check we actually overflowed.
87-
let max_value = u64::MAX as u128;
88-
assert!((payment_size_msat as u128 * opening_fee_proportional as u128) > max_value);
89-
}
90-
}
91-
}
92-
}

lightning-liquidity/src/lsps5/url_utils.rs

Lines changed: 0 additions & 135 deletions
Original file line numberDiff line numberDiff line change
@@ -102,138 +102,3 @@ impl Readable for LSPSUrl {
102102
Ok(Self(Readable::read(reader)?))
103103
}
104104
}
105-
106-
#[cfg(test)]
107-
mod tests {
108-
use super::*;
109-
use crate::alloc::string::ToString;
110-
use alloc::vec::Vec;
111-
use proptest::prelude::*;
112-
113-
#[test]
114-
fn test_extremely_long_url() {
115-
let url_str = format!("https://{}/path", "a".repeat(1000)).to_string();
116-
let url_chars = url_str.chars().count();
117-
let result = LSPSUrl::parse(url_str);
118-
119-
assert!(result.is_ok());
120-
let url = result.unwrap();
121-
assert_eq!(url.0 .0.chars().count(), url_chars);
122-
}
123-
124-
#[test]
125-
fn test_parse_http_url() {
126-
let url_str = "http://example.com/path".to_string();
127-
let url = LSPSUrl::parse(url_str).unwrap_err();
128-
assert_eq!(url, LSPS5ProtocolError::UnsupportedProtocol);
129-
}
130-
131-
#[test]
132-
fn valid_lsps_url() {
133-
let test_vec: Vec<&'static str> = vec![
134-
"https://www.example.org/push?l=1234567890abcopqrstuv&c=best",
135-
"https://www.example.com/path",
136-
"https://example.org",
137-
"https://example.com:8080/path",
138-
"https://api.example.com/v1/resources",
139-
"https://example.com/page#section1",
140-
"https://example.com/search?q=test#results",
141-
"https://user:pass@example.com/",
142-
"https://192.168.1.1/admin",
143-
"https://example.com://path",
144-
"https://example.com/path%20with%20spaces",
145-
"https://example_example.com/path?query=with&spaces=true",
146-
];
147-
for url_str in test_vec {
148-
let url = LSPSUrl::parse(url_str.to_string());
149-
assert!(url.is_ok(), "Failed to parse URL: {}", url_str);
150-
}
151-
}
152-
153-
#[test]
154-
fn invalid_lsps_url() {
155-
let test_vec = vec![
156-
"ftp://ftp.example.org/pub/files/document.pdf",
157-
"sftp://user:password@sftp.example.com:22/uploads/",
158-
"ssh://username@host.com:2222",
159-
"lightning://03a.example.com/invoice?amount=10000",
160-
"ftp://user@ftp.example.com/files/",
161-
"https://例子.测试/path",
162-
"a123+-.://example.com",
163-
"a123+-.://example.com",
164-
"https:\\\\example.com\\path",
165-
"https:///whatever",
166-
"https://example.com/path with spaces",
167-
];
168-
for url_str in test_vec {
169-
let url = LSPSUrl::parse(url_str.to_string());
170-
assert!(url.is_err(), "Expected error for URL: {}", url_str);
171-
}
172-
}
173-
174-
#[test]
175-
fn parsing_errors() {
176-
let test_vec = vec![
177-
"example.com/path",
178-
"https://bad domain.com/",
179-
"https://example.com\0/path",
180-
"https://",
181-
"ht@ps://example.com",
182-
"http!://example.com",
183-
"1https://example.com",
184-
"https://://example.com",
185-
"https://example.com:port/path",
186-
"https://:8080/path",
187-
"https:",
188-
"://",
189-
"https://example.com\0/path",
190-
];
191-
for url_str in test_vec {
192-
let url = LSPSUrl::parse(url_str.to_string());
193-
assert!(url.is_err(), "Expected error for URL: {}", url_str);
194-
}
195-
}
196-
197-
fn host_strategy() -> impl Strategy<Value = String> {
198-
prop_oneof![
199-
proptest::string::string_regex(
200-
"[a-z0-9]+(?:-[a-z0-9]+)*(?:\\.[a-z0-9]+(?:-[a-z0-9]+)*)*"
201-
)
202-
.unwrap(),
203-
(0u8..=255u8, 0u8..=255u8, 0u8..=255u8, 0u8..=255u8)
204-
.prop_map(|(a, b, c, d)| format!("{}.{}.{}.{}", a, b, c, d))
205-
]
206-
}
207-
208-
proptest! {
209-
#[test]
210-
fn proptest_parse_round_trip(
211-
host in host_strategy(),
212-
port in proptest::option::of(0u16..=65535u16),
213-
path in proptest::option::of(proptest::string::string_regex("[a-zA-Z0-9._%&=:@/-]{0,20}").unwrap()),
214-
query in proptest::option::of(proptest::string::string_regex("[a-zA-Z0-9._%&=:@/-]{0,20}").unwrap()),
215-
fragment in proptest::option::of(proptest::string::string_regex("[a-zA-Z0-9._%&=:@/-]{0,20}").unwrap())
216-
) {
217-
let mut url = format!("https://{}", host);
218-
if let Some(p) = port {
219-
url.push_str(&format!(":{}", p));
220-
}
221-
if let Some(pth) = &path {
222-
url.push('/');
223-
url.push_str(pth);
224-
}
225-
if let Some(q) = &query {
226-
url.push('?');
227-
url.push_str(q);
228-
}
229-
if let Some(f) = &fragment {
230-
url.push('#');
231-
url.push_str(f);
232-
}
233-
234-
let parsed = LSPSUrl::parse(url.clone()).expect("should parse");
235-
prop_assert_eq!(parsed.url(), url.as_str());
236-
prop_assert_eq!(parsed.url_length(), url.chars().count());
237-
}
238-
}
239-
}

0 commit comments

Comments
 (0)