Skip to content
This repository was archived by the owner on Jan 6, 2025. It is now read-only.

Commit 587ffe7

Browse files
authored
Merge pull request #56 from tnull/2023-11-add-proptest
2 parents 8cd4a4b + 77d9078 commit 587ffe7

File tree

5 files changed

+109
-43
lines changed

5 files changed

+109
-43
lines changed

.github/workflows/build.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ jobs:
2828
- name: Pin crates for MSRV
2929
if: matrix.msrv
3030
run: |
31-
# No need to pin currently
31+
cargo update -p proptest --precise "1.2.0" --verbose # proptest 1.3.0 requires rustc 1.64.0
3232
- name: Cargo check
3333
run: cargo check --release
3434
- name: Check documentation

Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,6 @@ core2 = { version = "0.3.0", optional = true, default-features = false }
2222
chrono = { version = "0.4", default-features = false, features = ["serde", "alloc"] }
2323
serde = { version = "1.0", default-features = false, features = ["derive", "alloc"] }
2424
serde_json = "1.0"
25+
26+
[dev-dependencies]
27+
proptest = "1.0.0"
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# Seeds for failure cases proptest has generated in the past. It is
2+
# automatically read and these particular cases re-run before any
3+
# novel cases are generated.
4+
#
5+
# It is recommended to check this file in to source control so that
6+
# everyone who runs the test benefits from these saved cases.

src/lsps2/service.rs

Lines changed: 73 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -779,67 +779,98 @@ where
779779
fn calculate_amount_to_forward_per_htlc(
780780
htlcs: &[InterceptedHTLC], total_amt_to_forward_msat: u64,
781781
) -> Vec<(InterceptId, u64)> {
782+
// TODO: we should eventually make sure the HTLCs are all above ChannelDetails::next_outbound_minimum_msat
782783
let total_received_msat: u64 =
783784
htlcs.iter().map(|htlc| htlc.expected_outbound_amount_msat).sum();
784785

785-
let mut fee_remaining_msat = total_received_msat - total_amt_to_forward_msat;
786-
let total_fee_msat = fee_remaining_msat;
786+
match total_received_msat.checked_sub(total_amt_to_forward_msat) {
787+
Some(total_fee_msat) => {
788+
let mut fee_remaining_msat = total_fee_msat;
787789

788-
let mut per_htlc_forwards = vec![];
790+
let mut per_htlc_forwards = vec![];
789791

790-
for (index, htlc) in htlcs.iter().enumerate() {
791-
let proportional_fee_amt_msat =
792-
total_fee_msat * htlc.expected_outbound_amount_msat / total_received_msat;
792+
for (index, htlc) in htlcs.iter().enumerate() {
793+
let proportional_fee_amt_msat =
794+
total_fee_msat * (htlc.expected_outbound_amount_msat / total_received_msat);
793795

794-
let mut actual_fee_amt_msat = core::cmp::min(fee_remaining_msat, proportional_fee_amt_msat);
795-
fee_remaining_msat -= actual_fee_amt_msat;
796+
let mut actual_fee_amt_msat =
797+
core::cmp::min(fee_remaining_msat, proportional_fee_amt_msat);
798+
fee_remaining_msat -= actual_fee_amt_msat;
796799

797-
if index == htlcs.len() - 1 {
798-
actual_fee_amt_msat += fee_remaining_msat;
799-
}
800+
if index == htlcs.len() - 1 {
801+
actual_fee_amt_msat += fee_remaining_msat;
802+
}
800803

801-
let amount_to_forward_msat = htlc.expected_outbound_amount_msat - actual_fee_amt_msat;
804+
let amount_to_forward_msat =
805+
htlc.expected_outbound_amount_msat.saturating_sub(actual_fee_amt_msat);
802806

803-
per_htlc_forwards.push((htlc.intercept_id, amount_to_forward_msat))
804-
}
807+
per_htlc_forwards.push((htlc.intercept_id, amount_to_forward_msat))
808+
}
805809

806-
per_htlc_forwards
810+
per_htlc_forwards
811+
}
812+
None => Vec::new(),
813+
}
807814
}
808815

809816
#[cfg(test)]
810817
mod tests {
811818

812819
use super::*;
820+
use proptest::prelude::*;
813821

814-
#[test]
815-
fn test_calculate_amount_to_forward() {
816-
// TODO: Use proptest to generate random allocations
817-
let htlcs = vec![
818-
InterceptedHTLC {
819-
intercept_id: InterceptId([0; 32]),
820-
expected_outbound_amount_msat: 1000,
821-
},
822-
InterceptedHTLC {
823-
intercept_id: InterceptId([1; 32]),
824-
expected_outbound_amount_msat: 2000,
825-
},
826-
InterceptedHTLC {
827-
intercept_id: InterceptId([2; 32]),
828-
expected_outbound_amount_msat: 3000,
829-
},
830-
];
831-
832-
let total_amt_to_forward_msat = 5000;
833-
834-
let result = calculate_amount_to_forward_per_htlc(&htlcs, total_amt_to_forward_msat);
822+
const MAX_VALUE_MSAT: u64 = 21_000_000_0000_0000_000;
835823

836-
assert_eq!(result[0].0, htlcs[0].intercept_id);
837-
assert_eq!(result[0].1, 834);
824+
fn arb_forward_amounts() -> impl Strategy<Value = (u64, u64, u64, u64)> {
825+
(1u64..MAX_VALUE_MSAT, 1u64..MAX_VALUE_MSAT, 1u64..MAX_VALUE_MSAT, 1u64..MAX_VALUE_MSAT)
826+
.prop_map(|(a, b, c, d)| {
827+
(a, b, c, core::cmp::min(d, a.saturating_add(b).saturating_add(c)))
828+
})
829+
}
838830

839-
assert_eq!(result[1].0, htlcs[1].intercept_id);
840-
assert_eq!(result[1].1, 1667);
831+
proptest! {
832+
#[test]
833+
fn test_calculate_amount_to_forward((o_0, o_1, o_2, total_amt_to_forward_msat) in arb_forward_amounts()) {
834+
let htlcs = vec![
835+
InterceptedHTLC {
836+
intercept_id: InterceptId([0; 32]),
837+
expected_outbound_amount_msat: o_0
838+
},
839+
InterceptedHTLC {
840+
intercept_id: InterceptId([1; 32]),
841+
expected_outbound_amount_msat: o_1
842+
},
843+
InterceptedHTLC {
844+
intercept_id: InterceptId([2; 32]),
845+
expected_outbound_amount_msat: o_2
846+
},
847+
];
848+
849+
let result = calculate_amount_to_forward_per_htlc(&htlcs, total_amt_to_forward_msat);
850+
let total_received_msat = o_0 + o_1 + o_2;
851+
852+
if total_received_msat < total_amt_to_forward_msat {
853+
assert_eq!(result.len(), 0);
854+
} else {
855+
assert_ne!(result.len(), 0);
856+
assert_eq!(result[0].0, htlcs[0].intercept_id);
857+
assert_eq!(result[1].0, htlcs[1].intercept_id);
858+
assert_eq!(result[2].0, htlcs[2].intercept_id);
859+
assert!(result[0].1 <= o_0);
860+
assert!(result[1].1 <= o_1);
861+
assert!(result[2].1 <= o_2);
862+
863+
let result_sum = result.iter().map(|(_, f)| f).sum::<u64>();
864+
assert!(result_sum >= total_amt_to_forward_msat);
865+
let five_pct = result_sum as f32 * 0.1;
866+
let fair_share_0 = ((o_0 as f32 / total_received_msat as f32) * result_sum as f32).max(o_0 as f32);
867+
assert!(result[0].1 as f32 <= fair_share_0 + five_pct);
868+
let fair_share_1 = ((o_1 as f32 / total_received_msat as f32) * result_sum as f32).max(o_1 as f32);
869+
assert!(result[1].1 as f32 <= fair_share_1 + five_pct);
870+
let fair_share_2 = ((o_2 as f32 / total_received_msat as f32) * result_sum as f32).max(o_2 as f32);
871+
assert!(result[2].1 as f32 <= fair_share_2 + five_pct);
872+
}
841873

842-
assert_eq!(result[2].0, htlcs[2].intercept_id);
843-
assert_eq!(result[2].1, 2499);
874+
}
844875
}
845876
}

src/lsps2/utils.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,3 +54,29 @@ pub fn compute_opening_fee(
5454
.and_then(|f| f.checked_div(1000000))
5555
.map(|f| core::cmp::max(f, opening_fee_min_fee_msat))
5656
}
57+
58+
#[cfg(test)]
59+
mod tests {
60+
use super::*;
61+
use proptest::prelude::*;
62+
63+
const MAX_VALUE_MSAT: u64 = 21_000_000_0000_0000_000;
64+
65+
fn arb_opening_fee_params() -> impl Strategy<Value = (u64, u64, u64)> {
66+
(0u64..MAX_VALUE_MSAT, 0u64..MAX_VALUE_MSAT, 0u64..MAX_VALUE_MSAT)
67+
}
68+
69+
proptest! {
70+
#[test]
71+
fn test_compute_opening_fee((payment_size_msat, opening_fee_min_fee_msat, opening_fee_proportional) in arb_opening_fee_params()) {
72+
if let Some(res) = compute_opening_fee(payment_size_msat, opening_fee_min_fee_msat, opening_fee_proportional) {
73+
assert!(res >= opening_fee_min_fee_msat);
74+
assert_eq!(res as f32, (payment_size_msat as f32 * opening_fee_proportional as f32));
75+
} else {
76+
// Check we actually overflowed.
77+
let max_value = u64::MAX as u128;
78+
assert!((payment_size_msat as u128 * opening_fee_proportional as u128) > max_value);
79+
}
80+
}
81+
}
82+
}

0 commit comments

Comments
 (0)