Skip to content

Commit 37728cb

Browse files
committed
lsps2: Validate gross proportional fees
Base proportional fee limits on the amount sent by the payer. The claimable amount excludes the LSP's withheld fee and otherwise rejects a valid fee at the configured limit. Co-Authored-By: HAL 9000
1 parent 3865bbe commit 37728cb

1 file changed

Lines changed: 59 additions & 14 deletions

File tree

src/event.rs

Lines changed: 59 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -664,26 +664,40 @@ where
664664
}
665665
}
666666

667-
fn lsps2_max_total_opening_fee_msat(payment_metadata: &[u8], amount_msat: u64) -> Option<u64> {
667+
fn lsps2_max_total_opening_fee_msat(
668+
payment_metadata: &[u8], amount_msat: u64, counterparty_skimmed_fee_msat: u64,
669+
) -> Option<u64> {
668670
let metadata = PaymentMetadata::read(&mut &payment_metadata[..]).ok()?;
669-
Self::lsps2_max_total_opening_fee_msat_from_metadata(metadata, amount_msat)
671+
Self::lsps2_max_total_opening_fee_msat_from_metadata(
672+
metadata,
673+
amount_msat,
674+
counterparty_skimmed_fee_msat,
675+
)
670676
}
671677

672678
fn lsps2_bolt12_max_total_opening_fee_msat(
673679
payment_metadata: &BTreeMap<u64, Vec<u8>>, amount_msat: u64,
680+
counterparty_skimmed_fee_msat: u64,
674681
) -> Option<u64> {
675682
let metadata = PaymentMetadata::decode_from_bolt12_payment_metadata(payment_metadata)?;
676-
Self::lsps2_max_total_opening_fee_msat_from_metadata(metadata, amount_msat)
683+
Self::lsps2_max_total_opening_fee_msat_from_metadata(
684+
metadata,
685+
amount_msat,
686+
counterparty_skimmed_fee_msat,
687+
)
677688
}
678689

679690
fn lsps2_max_total_opening_fee_msat_from_metadata(
680-
metadata: PaymentMetadata, amount_msat: u64,
691+
metadata: PaymentMetadata, amount_msat: u64, counterparty_skimmed_fee_msat: u64,
681692
) -> Option<u64> {
682693
let lsps2_parameters = metadata.lsps2_parameters?;
683694
lsps2_parameters.max_total_opening_fee_msat.or_else(|| {
684695
lsps2_parameters.max_proportional_opening_fee_ppm_msat.and_then(|max_prop_fee| {
685-
// If it's a variable amount payment, compute the actual fee.
686-
compute_opening_fee(amount_msat, 0, max_prop_fee)
696+
// `PaymentClaimable::amount_msat` excludes the fee withheld by the LSP. LSPS2
697+
// proportional fees are calculated from the amount sent by the payer, so add the
698+
// withheld fee back before computing the permitted fee.
699+
let payment_size_msat = amount_msat.checked_add(counterparty_skimmed_fee_msat)?;
700+
compute_opening_fee(payment_size_msat, 0, max_prop_fee)
687701
})
688702
})
689703
}
@@ -873,11 +887,19 @@ where
873887
.as_ref()
874888
.and_then(|fields| fields.payment_metadata.as_ref())
875889
.and_then(|metadata| {
876-
Self::lsps2_max_total_opening_fee_msat(metadata, amount_msat)
890+
Self::lsps2_max_total_opening_fee_msat(
891+
metadata,
892+
amount_msat,
893+
counterparty_skimmed_fee_msat,
894+
)
877895
}),
878896
PaymentPurpose::Bolt12OfferPayment { payment_context, .. } => {
879897
payment_context.payment_metadata.as_ref().and_then(|metadata| {
880-
Self::lsps2_bolt12_max_total_opening_fee_msat(metadata, amount_msat)
898+
Self::lsps2_bolt12_max_total_opening_fee_msat(
899+
metadata,
900+
amount_msat,
901+
counterparty_skimmed_fee_msat,
902+
)
881903
})
882904
},
883905
_ => None,
@@ -2074,12 +2096,33 @@ mod tests {
20742096
assert_eq!(
20752097
EventHandler::<Arc<TestLogger>>::lsps2_max_total_opening_fee_msat(
20762098
&metadata.encode(),
2077-
100_000
2099+
100_000,
2100+
0,
20782101
),
20792102
Some(42_000)
20802103
);
20812104
}
20822105

2106+
#[test]
2107+
fn lsps2_proportional_fee_uses_gross_payment_amount() {
2108+
let metadata = PaymentMetadata {
2109+
lsps2_parameters: Some(LSPS2Parameters {
2110+
max_total_opening_fee_msat: None,
2111+
max_proportional_opening_fee_ppm_msat: Some(10_000),
2112+
}),
2113+
lsps2_lease_parameters: None,
2114+
};
2115+
2116+
assert_eq!(
2117+
EventHandler::<Arc<TestLogger>>::lsps2_max_total_opening_fee_msat(
2118+
&metadata.encode(),
2119+
198_000,
2120+
2_000,
2121+
),
2122+
Some(2_000)
2123+
);
2124+
}
2125+
20832126
#[test]
20842127
fn lsps2_payment_metadata_missing_or_malformed_limit_is_rejected() {
20852128
let empty_metadata =
@@ -2096,18 +2139,20 @@ mod tests {
20962139
assert_eq!(
20972140
EventHandler::<Arc<TestLogger>>::lsps2_max_total_opening_fee_msat(
20982141
&empty_metadata,
2099-
100_000
2142+
100_000,
2143+
0,
21002144
),
21012145
None
21022146
);
21032147
assert_eq!(
2104-
EventHandler::<Arc<TestLogger>>::lsps2_max_total_opening_fee_msat(&[0xff], 100_000),
2148+
EventHandler::<Arc<TestLogger>>::lsps2_max_total_opening_fee_msat(&[0xff], 100_000, 0,),
21052149
None
21062150
);
21072151
assert_eq!(
21082152
EventHandler::<Arc<TestLogger>>::lsps2_max_total_opening_fee_msat(
21092153
&metadata_without_fee_limit,
2110-
100_000
2154+
100_000,
2155+
0,
21112156
),
21122157
None
21132158
);
@@ -2126,7 +2171,7 @@ mod tests {
21262171

21272172
assert_eq!(
21282173
EventHandler::<Arc<TestLogger>>::lsps2_bolt12_max_total_opening_fee_msat(
2129-
&metadata, 100_000
2174+
&metadata, 100_000, 0,
21302175
),
21312176
Some(1_000)
21322177
);
@@ -2144,7 +2189,7 @@ mod tests {
21442189
for metadata in [empty_metadata, malformed_metadata, metadata_without_fee_limit] {
21452190
assert_eq!(
21462191
EventHandler::<Arc<TestLogger>>::lsps2_bolt12_max_total_opening_fee_msat(
2147-
&metadata, 100_000
2192+
&metadata, 100_000, 0,
21482193
),
21492194
None
21502195
);

0 commit comments

Comments
 (0)