amount.rs 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101
  1. //! CDK Amount
  2. //!
  3. //! Is any unit and will be treated as the unit of the wallet
  4. use std::cmp::Ordering;
  5. use std::collections::HashMap;
  6. use std::fmt;
  7. use std::str::FromStr;
  8. use lightning::offers::offer::Offer;
  9. use serde::{Deserialize, Serialize};
  10. use thiserror::Error;
  11. use crate::nuts::CurrencyUnit;
  12. use crate::Id;
  13. /// Amount Error
  14. #[derive(Debug, Error)]
  15. pub enum Error {
  16. /// Split Values must be less then or equal to amount
  17. #[error("Split Values must be less then or equal to amount")]
  18. SplitValuesGreater,
  19. /// Amount overflow
  20. #[error("Amount Overflow")]
  21. AmountOverflow,
  22. /// Cannot convert units
  23. #[error("Cannot convert units")]
  24. CannotConvertUnits,
  25. /// Invalid amount
  26. #[error("Invalid Amount: {0}")]
  27. InvalidAmount(String),
  28. /// Amount undefined
  29. #[error("Amount undefined")]
  30. AmountUndefined,
  31. /// Utf8 parse error
  32. #[error(transparent)]
  33. Utf8ParseError(#[from] std::string::FromUtf8Error),
  34. }
  35. /// Amount can be any unit
  36. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
  37. #[cfg_attr(feature = "swagger", derive(utoipa::ToSchema))]
  38. #[serde(transparent)]
  39. pub struct Amount(u64);
  40. /// Fees and and amount type, it can be casted just as a reference to the inner amounts, or a single
  41. /// u64 which is the fee
  42. #[derive(Debug, Clone)]
  43. pub struct FeeAndAmounts {
  44. fee: u64,
  45. amounts: Vec<u64>,
  46. }
  47. impl From<(u64, Vec<u64>)> for FeeAndAmounts {
  48. fn from(value: (u64, Vec<u64>)) -> Self {
  49. Self {
  50. fee: value.0,
  51. amounts: value.1,
  52. }
  53. }
  54. }
  55. impl FeeAndAmounts {
  56. /// Fees
  57. #[inline(always)]
  58. pub fn fee(&self) -> u64 {
  59. self.fee
  60. }
  61. /// Amounts
  62. #[inline(always)]
  63. pub fn amounts(&self) -> &[u64] {
  64. &self.amounts
  65. }
  66. }
  67. /// Fees and Amounts for each Keyset
  68. pub type KeysetFeeAndAmounts = HashMap<Id, FeeAndAmounts>;
  69. impl FromStr for Amount {
  70. type Err = Error;
  71. fn from_str(s: &str) -> Result<Self, Self::Err> {
  72. let value = s
  73. .parse::<u64>()
  74. .map_err(|_| Error::InvalidAmount(s.to_owned()))?;
  75. Ok(Amount(value))
  76. }
  77. }
  78. impl Amount {
  79. /// Amount zero
  80. pub const ZERO: Amount = Amount(0);
  81. /// Amount one
  82. pub const ONE: Amount = Amount(1);
  83. /// Split into parts that are powers of two
  84. pub fn split(&self, fee_and_amounts: &FeeAndAmounts) -> Vec<Self> {
  85. fee_and_amounts
  86. .amounts
  87. .iter()
  88. .rev()
  89. .fold((Vec::new(), self.0), |(mut acc, total), &amount| {
  90. if total >= amount {
  91. acc.push(Self::from(amount));
  92. }
  93. (acc, total % amount)
  94. })
  95. .0
  96. }
  97. /// Split into parts that are powers of two by target
  98. pub fn split_targeted(
  99. &self,
  100. target: &SplitTarget,
  101. fee_and_amounts: &FeeAndAmounts,
  102. ) -> Result<Vec<Self>, Error> {
  103. let mut parts = match target {
  104. SplitTarget::None => self.split(fee_and_amounts),
  105. SplitTarget::Value(amount) => {
  106. if self.le(amount) {
  107. return Ok(self.split(fee_and_amounts));
  108. }
  109. let mut parts_total = Amount::ZERO;
  110. let mut parts = Vec::new();
  111. // The powers of two that are need to create target value
  112. let parts_of_value = amount.split(fee_and_amounts);
  113. while parts_total.lt(self) {
  114. for part in parts_of_value.iter().copied() {
  115. if (part + parts_total).le(self) {
  116. parts.push(part);
  117. } else {
  118. let amount_left = *self - parts_total;
  119. parts.extend(amount_left.split(fee_and_amounts));
  120. }
  121. parts_total = Amount::try_sum(parts.clone().iter().copied())?;
  122. if parts_total.eq(self) {
  123. break;
  124. }
  125. }
  126. }
  127. parts
  128. }
  129. SplitTarget::Values(values) => {
  130. let values_total: Amount = Amount::try_sum(values.clone().into_iter())?;
  131. match self.cmp(&values_total) {
  132. Ordering::Equal => values.clone(),
  133. Ordering::Less => {
  134. return Err(Error::SplitValuesGreater);
  135. }
  136. Ordering::Greater => {
  137. let extra = *self - values_total;
  138. let mut extra_amount = extra.split(fee_and_amounts);
  139. let mut values = values.clone();
  140. values.append(&mut extra_amount);
  141. values
  142. }
  143. }
  144. }
  145. };
  146. parts.sort();
  147. Ok(parts)
  148. }
  149. /// Splits amount into powers of two while accounting for the swap fee
  150. pub fn split_with_fee(&self, fee_and_amounts: &FeeAndAmounts) -> Result<Vec<Self>, Error> {
  151. let without_fee_amounts = self.split(fee_and_amounts);
  152. let total_fee_ppk = fee_and_amounts
  153. .fee
  154. .checked_mul(without_fee_amounts.len() as u64)
  155. .ok_or(Error::AmountOverflow)?;
  156. let fee = Amount::from(total_fee_ppk.div_ceil(1000));
  157. let new_amount = self.checked_add(fee).ok_or(Error::AmountOverflow)?;
  158. let split = new_amount.split(fee_and_amounts);
  159. let split_fee_ppk = (split.len() as u64)
  160. .checked_mul(fee_and_amounts.fee)
  161. .ok_or(Error::AmountOverflow)?;
  162. let split_fee = Amount::from(split_fee_ppk.div_ceil(1000));
  163. if let Some(net_amount) = new_amount.checked_sub(split_fee) {
  164. if net_amount >= *self {
  165. return Ok(split);
  166. }
  167. }
  168. self.checked_add(Amount::ONE)
  169. .ok_or(Error::AmountOverflow)?
  170. .split_with_fee(fee_and_amounts)
  171. }
  172. /// Checked addition for Amount. Returns None if overflow occurs.
  173. pub fn checked_add(self, other: Amount) -> Option<Amount> {
  174. self.0.checked_add(other.0).map(Amount)
  175. }
  176. /// Checked subtraction for Amount. Returns None if overflow occurs.
  177. pub fn checked_sub(self, other: Amount) -> Option<Amount> {
  178. self.0.checked_sub(other.0).map(Amount)
  179. }
  180. /// Checked multiplication for Amount. Returns None if overflow occurs.
  181. pub fn checked_mul(self, other: Amount) -> Option<Amount> {
  182. self.0.checked_mul(other.0).map(Amount)
  183. }
  184. /// Checked division for Amount. Returns None if overflow occurs.
  185. pub fn checked_div(self, other: Amount) -> Option<Amount> {
  186. self.0.checked_div(other.0).map(Amount)
  187. }
  188. /// Try sum to check for overflow
  189. pub fn try_sum<I>(iter: I) -> Result<Self, Error>
  190. where
  191. I: IntoIterator<Item = Self>,
  192. {
  193. iter.into_iter().try_fold(Amount::ZERO, |acc, x| {
  194. acc.checked_add(x).ok_or(Error::AmountOverflow)
  195. })
  196. }
  197. /// Convert unit
  198. pub fn convert_unit(
  199. &self,
  200. current_unit: &CurrencyUnit,
  201. target_unit: &CurrencyUnit,
  202. ) -> Result<Amount, Error> {
  203. to_unit(self.0, current_unit, target_unit)
  204. }
  205. ///
  206. /// Convert to u64
  207. pub fn to_u64(self) -> u64 {
  208. self.0
  209. }
  210. /// Convert to i64
  211. pub fn to_i64(self) -> Option<i64> {
  212. if self.0 <= i64::MAX as u64 {
  213. Some(self.0 as i64)
  214. } else {
  215. None
  216. }
  217. }
  218. /// Create from i64, returning None if negative
  219. pub fn from_i64(value: i64) -> Option<Self> {
  220. if value >= 0 {
  221. Some(Amount(value as u64))
  222. } else {
  223. None
  224. }
  225. }
  226. }
  227. impl Default for Amount {
  228. fn default() -> Self {
  229. Amount::ZERO
  230. }
  231. }
  232. impl Default for &Amount {
  233. fn default() -> Self {
  234. &Amount::ZERO
  235. }
  236. }
  237. impl fmt::Display for Amount {
  238. fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
  239. if let Some(width) = f.width() {
  240. write!(f, "{:width$}", self.0, width = width)
  241. } else {
  242. write!(f, "{}", self.0)
  243. }
  244. }
  245. }
  246. impl From<u64> for Amount {
  247. fn from(value: u64) -> Self {
  248. Self(value)
  249. }
  250. }
  251. impl From<&u64> for Amount {
  252. fn from(value: &u64) -> Self {
  253. Self(*value)
  254. }
  255. }
  256. impl From<Amount> for u64 {
  257. fn from(value: Amount) -> Self {
  258. value.0
  259. }
  260. }
  261. impl AsRef<u64> for Amount {
  262. fn as_ref(&self) -> &u64 {
  263. &self.0
  264. }
  265. }
  266. impl std::ops::Add for Amount {
  267. type Output = Amount;
  268. fn add(self, rhs: Amount) -> Self::Output {
  269. self.checked_add(rhs)
  270. .expect("Addition overflow: the sum of the amounts exceeds the maximum value")
  271. }
  272. }
  273. impl std::ops::AddAssign for Amount {
  274. fn add_assign(&mut self, rhs: Self) {
  275. *self = self
  276. .checked_add(rhs)
  277. .expect("AddAssign overflow: the sum of the amounts exceeds the maximum value");
  278. }
  279. }
  280. impl std::ops::Sub for Amount {
  281. type Output = Amount;
  282. fn sub(self, rhs: Amount) -> Self::Output {
  283. self.checked_sub(rhs)
  284. .expect("Subtraction underflow: cannot subtract a larger amount from a smaller amount")
  285. }
  286. }
  287. impl std::ops::SubAssign for Amount {
  288. fn sub_assign(&mut self, other: Self) {
  289. *self = self
  290. .checked_sub(other)
  291. .expect("SubAssign underflow: cannot subtract a larger amount from a smaller amount");
  292. }
  293. }
  294. impl std::ops::Mul for Amount {
  295. type Output = Self;
  296. fn mul(self, other: Self) -> Self::Output {
  297. self.checked_mul(other)
  298. .expect("Multiplication overflow: the product of the amounts exceeds the maximum value")
  299. }
  300. }
  301. impl std::ops::Div for Amount {
  302. type Output = Self;
  303. fn div(self, other: Self) -> Self::Output {
  304. self.checked_div(other)
  305. .expect("Division error: cannot divide by zero or overflow occurred")
  306. }
  307. }
  308. /// Convert offer to amount in unit
  309. pub fn amount_for_offer(offer: &Offer, unit: &CurrencyUnit) -> Result<Amount, Error> {
  310. let offer_amount = offer.amount().ok_or(Error::AmountUndefined)?;
  311. let (amount, currency) = match offer_amount {
  312. lightning::offers::offer::Amount::Bitcoin { amount_msats } => {
  313. (amount_msats, CurrencyUnit::Msat)
  314. }
  315. lightning::offers::offer::Amount::Currency {
  316. iso4217_code,
  317. amount,
  318. } => (
  319. amount,
  320. CurrencyUnit::from_str(&String::from_utf8(iso4217_code.to_vec())?)
  321. .map_err(|_| Error::CannotConvertUnits)?,
  322. ),
  323. };
  324. to_unit(amount, &currency, unit).map_err(|_err| Error::CannotConvertUnits)
  325. }
  326. /// Kinds of targeting that are supported
  327. #[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Default, Serialize, Deserialize)]
  328. pub enum SplitTarget {
  329. /// Default target; least amount of proofs
  330. #[default]
  331. None,
  332. /// Target amount for wallet to have most proofs that add up to value
  333. Value(Amount),
  334. /// Specific amounts to split into **MUST** equal amount being split
  335. Values(Vec<Amount>),
  336. }
  337. /// Msats in sat
  338. pub const MSAT_IN_SAT: u64 = 1000;
  339. /// Helper function to convert units
  340. pub fn to_unit<T>(
  341. amount: T,
  342. current_unit: &CurrencyUnit,
  343. target_unit: &CurrencyUnit,
  344. ) -> Result<Amount, Error>
  345. where
  346. T: Into<u64>,
  347. {
  348. let amount = amount.into();
  349. match (current_unit, target_unit) {
  350. (CurrencyUnit::Sat, CurrencyUnit::Sat) => Ok(amount.into()),
  351. (CurrencyUnit::Msat, CurrencyUnit::Msat) => Ok(amount.into()),
  352. (CurrencyUnit::Sat, CurrencyUnit::Msat) => amount
  353. .checked_mul(MSAT_IN_SAT)
  354. .map(Amount::from)
  355. .ok_or(Error::AmountOverflow),
  356. (CurrencyUnit::Msat, CurrencyUnit::Sat) => Ok((amount / MSAT_IN_SAT).into()),
  357. (CurrencyUnit::Usd, CurrencyUnit::Usd) => Ok(amount.into()),
  358. (CurrencyUnit::Eur, CurrencyUnit::Eur) => Ok(amount.into()),
  359. _ => Err(Error::CannotConvertUnits),
  360. }
  361. }
  362. #[cfg(test)]
  363. mod tests {
  364. use super::*;
  365. #[test]
  366. fn test_split_amount() {
  367. let fee_and_amounts = (0, (0..32).map(|x| 2u64.pow(x)).collect::<Vec<_>>()).into();
  368. assert_eq!(
  369. Amount::from(1).split(&fee_and_amounts),
  370. vec![Amount::from(1)]
  371. );
  372. assert_eq!(
  373. Amount::from(2).split(&fee_and_amounts),
  374. vec![Amount::from(2)]
  375. );
  376. assert_eq!(
  377. Amount::from(3).split(&fee_and_amounts),
  378. vec![Amount::from(2), Amount::from(1)]
  379. );
  380. let amounts: Vec<Amount> = [8, 2, 1].iter().map(|a| Amount::from(*a)).collect();
  381. assert_eq!(Amount::from(11).split(&fee_and_amounts), amounts);
  382. let amounts: Vec<Amount> = [128, 64, 32, 16, 8, 4, 2, 1]
  383. .iter()
  384. .map(|a| Amount::from(*a))
  385. .collect();
  386. assert_eq!(Amount::from(255).split(&fee_and_amounts), amounts);
  387. }
  388. #[test]
  389. fn test_split_target_amount() {
  390. let fee_and_amounts = (0, (0..32).map(|x| 2u64.pow(x)).collect::<Vec<_>>()).into();
  391. let amount = Amount(65);
  392. let split = amount
  393. .split_targeted(&SplitTarget::Value(Amount(32)), &fee_and_amounts)
  394. .unwrap();
  395. assert_eq!(vec![Amount(1), Amount(32), Amount(32)], split);
  396. let amount = Amount(150);
  397. let split = amount
  398. .split_targeted(&SplitTarget::Value(Amount::from(50)), &fee_and_amounts)
  399. .unwrap();
  400. assert_eq!(
  401. vec![
  402. Amount(2),
  403. Amount(2),
  404. Amount(2),
  405. Amount(16),
  406. Amount(16),
  407. Amount(16),
  408. Amount(32),
  409. Amount(32),
  410. Amount(32)
  411. ],
  412. split
  413. );
  414. let amount = Amount::from(63);
  415. let split = amount
  416. .split_targeted(&SplitTarget::Value(Amount::from(32)), &fee_and_amounts)
  417. .unwrap();
  418. assert_eq!(
  419. vec![
  420. Amount(1),
  421. Amount(2),
  422. Amount(4),
  423. Amount(8),
  424. Amount(16),
  425. Amount(32)
  426. ],
  427. split
  428. );
  429. }
  430. #[test]
  431. fn test_split_with_fee() {
  432. let fee_and_amounts = (1, (0..32).map(|x| 2u64.pow(x)).collect::<Vec<_>>()).into();
  433. let amount = Amount(2);
  434. let split = amount.split_with_fee(&fee_and_amounts).unwrap();
  435. assert_eq!(split, vec![Amount(2), Amount(1)]);
  436. let amount = Amount(3);
  437. let split = amount.split_with_fee(&fee_and_amounts).unwrap();
  438. assert_eq!(split, vec![Amount(4)]);
  439. let amount = Amount(3);
  440. let fee_and_amounts = (1000, (0..32).map(|x| 2u64.pow(x)).collect::<Vec<_>>()).into();
  441. let split = amount.split_with_fee(&fee_and_amounts).unwrap();
  442. // With fee_ppk=1000 (100%), amount 3 requires proofs totaling at least 5
  443. // to cover both the amount (3) and fees (~2 for 2 proofs)
  444. assert_eq!(split, vec![Amount(4), Amount(1)]);
  445. }
  446. #[test]
  447. fn test_split_with_fee_reported_issue() {
  448. let fee_and_amounts = (100, (0..32).map(|x| 2u64.pow(x)).collect::<Vec<_>>()).into();
  449. // Test the reported issue: mint 600, send 300 with fee_ppk=100
  450. let amount = Amount(300);
  451. let split = amount.split_with_fee(&fee_and_amounts).unwrap();
  452. // Calculate the total fee for the split
  453. let total_fee_ppk = (split.len() as u64) * fee_and_amounts.fee;
  454. let total_fee = Amount::from(total_fee_ppk.div_ceil(1000));
  455. // The split should cover the amount plus fees
  456. let split_total = Amount::try_sum(split.iter().copied()).unwrap();
  457. assert!(
  458. split_total >= amount + total_fee,
  459. "Split total {} should be >= amount {} + fee {}",
  460. split_total,
  461. amount,
  462. total_fee
  463. );
  464. }
  465. #[test]
  466. fn test_split_with_fee_edge_cases() {
  467. // Test various amounts with fee_ppk=100
  468. let test_cases = vec![
  469. (Amount(1), 100),
  470. (Amount(10), 100),
  471. (Amount(50), 100),
  472. (Amount(100), 100),
  473. (Amount(200), 100),
  474. (Amount(300), 100),
  475. (Amount(500), 100),
  476. (Amount(600), 100),
  477. (Amount(1000), 100),
  478. (Amount(1337), 100),
  479. (Amount(5000), 100),
  480. ];
  481. for (amount, fee_ppk) in test_cases {
  482. let fee_and_amounts =
  483. (fee_ppk, (0..32).map(|x| 2u64.pow(x)).collect::<Vec<_>>()).into();
  484. let result = amount.split_with_fee(&fee_and_amounts);
  485. assert!(
  486. result.is_ok(),
  487. "split_with_fee failed for amount {} with fee_ppk {}: {:?}",
  488. amount,
  489. fee_ppk,
  490. result.err()
  491. );
  492. let split = result.unwrap();
  493. // Verify the split covers the required amount
  494. let split_total = Amount::try_sum(split.iter().copied()).unwrap();
  495. let fee_for_split = (split.len() as u64) * fee_ppk;
  496. let total_fee = Amount::from(fee_for_split.div_ceil(1000));
  497. // The net amount after fees should be at least the original amount
  498. let net_amount = split_total.checked_sub(total_fee);
  499. assert!(
  500. net_amount.is_some(),
  501. "Net amount calculation failed for amount {} with fee_ppk {}",
  502. amount,
  503. fee_ppk
  504. );
  505. assert!(
  506. net_amount.unwrap() >= amount,
  507. "Net amount {} is less than required {} for amount {} with fee_ppk {}",
  508. net_amount.unwrap(),
  509. amount,
  510. amount,
  511. fee_ppk
  512. );
  513. }
  514. }
  515. #[test]
  516. fn test_split_with_fee_high_fees() {
  517. // Test with very high fees
  518. let test_cases = vec![
  519. (Amount(10), 500), // 50% fee
  520. (Amount(10), 1000), // 100% fee
  521. (Amount(10), 2000), // 200% fee
  522. (Amount(100), 500),
  523. (Amount(100), 1000),
  524. (Amount(100), 2000),
  525. ];
  526. for (amount, fee_ppk) in test_cases {
  527. let fee_and_amounts =
  528. (fee_ppk, (0..32).map(|x| 2u64.pow(x)).collect::<Vec<_>>()).into();
  529. let result = amount.split_with_fee(&fee_and_amounts);
  530. assert!(
  531. result.is_ok(),
  532. "split_with_fee failed for amount {} with fee_ppk {}: {:?}",
  533. amount,
  534. fee_ppk,
  535. result.err()
  536. );
  537. let split = result.unwrap();
  538. let split_total = Amount::try_sum(split.iter().copied()).unwrap();
  539. // With high fees, we just need to ensure we can cover the amount
  540. assert!(
  541. split_total > amount,
  542. "Split total {} should be greater than amount {} for fee_ppk {}",
  543. split_total,
  544. amount,
  545. fee_ppk
  546. );
  547. }
  548. }
  549. #[test]
  550. fn test_split_with_fee_recursion_limit() {
  551. // Test that the recursion doesn't go infinite
  552. // This tests the edge case where the method keeps adding Amount::ONE
  553. let amount = Amount(1);
  554. let fee_ppk = 10000;
  555. let fee_and_amounts = (fee_ppk, (0..32).map(|x| 2u64.pow(x)).collect::<Vec<_>>()).into();
  556. let result = amount.split_with_fee(&fee_and_amounts);
  557. assert!(
  558. result.is_ok(),
  559. "split_with_fee should handle extreme fees without infinite recursion"
  560. );
  561. }
  562. #[test]
  563. fn test_split_values() {
  564. let fee_and_amounts = (0, (0..32).map(|x| 2u64.pow(x)).collect::<Vec<_>>()).into();
  565. let amount = Amount(10);
  566. let target = vec![Amount(2), Amount(4), Amount(4)];
  567. let split_target = SplitTarget::Values(target.clone());
  568. let values = amount
  569. .split_targeted(&split_target, &fee_and_amounts)
  570. .unwrap();
  571. assert_eq!(target, values);
  572. let target = vec![Amount(2), Amount(4), Amount(4)];
  573. let split_target = SplitTarget::Values(vec![Amount(2), Amount(4)]);
  574. let values = amount
  575. .split_targeted(&split_target, &fee_and_amounts)
  576. .unwrap();
  577. assert_eq!(target, values);
  578. let split_target = SplitTarget::Values(vec![Amount(2), Amount(10)]);
  579. let values = amount.split_targeted(&split_target, &fee_and_amounts);
  580. assert!(values.is_err())
  581. }
  582. #[test]
  583. #[should_panic]
  584. fn test_amount_addition() {
  585. let amount_one: Amount = u64::MAX.into();
  586. let amount_two: Amount = 1.into();
  587. let amounts = vec![amount_one, amount_two];
  588. let _total: Amount = Amount::try_sum(amounts).unwrap();
  589. }
  590. #[test]
  591. fn test_try_amount_addition() {
  592. let amount_one: Amount = u64::MAX.into();
  593. let amount_two: Amount = 1.into();
  594. let amounts = vec![amount_one, amount_two];
  595. let total = Amount::try_sum(amounts);
  596. assert!(total.is_err());
  597. let amount_one: Amount = 10000.into();
  598. let amount_two: Amount = 1.into();
  599. let amounts = vec![amount_one, amount_two];
  600. let total = Amount::try_sum(amounts).unwrap();
  601. assert_eq!(total, 10001.into());
  602. }
  603. #[test]
  604. fn test_amount_to_unit() {
  605. let amount = Amount::from(1000);
  606. let current_unit = CurrencyUnit::Sat;
  607. let target_unit = CurrencyUnit::Msat;
  608. let converted = to_unit(amount, &current_unit, &target_unit).unwrap();
  609. assert_eq!(converted, 1000000.into());
  610. let amount = Amount::from(1000);
  611. let current_unit = CurrencyUnit::Msat;
  612. let target_unit = CurrencyUnit::Sat;
  613. let converted = to_unit(amount, &current_unit, &target_unit).unwrap();
  614. assert_eq!(converted, 1.into());
  615. let amount = Amount::from(1);
  616. let current_unit = CurrencyUnit::Usd;
  617. let target_unit = CurrencyUnit::Usd;
  618. let converted = to_unit(amount, &current_unit, &target_unit).unwrap();
  619. assert_eq!(converted, 1.into());
  620. let amount = Amount::from(1);
  621. let current_unit = CurrencyUnit::Eur;
  622. let target_unit = CurrencyUnit::Eur;
  623. let converted = to_unit(amount, &current_unit, &target_unit).unwrap();
  624. assert_eq!(converted, 1.into());
  625. let amount = Amount::from(1);
  626. let current_unit = CurrencyUnit::Sat;
  627. let target_unit = CurrencyUnit::Eur;
  628. let converted = to_unit(amount, &current_unit, &target_unit);
  629. assert!(converted.is_err());
  630. }
  631. /// Tests that the subtraction operator correctly computes the difference between amounts.
  632. ///
  633. /// This test verifies that the `-` operator for Amount produces the expected result.
  634. /// It's particularly important because the subtraction operation is used in critical
  635. /// code paths like `split_targeted`, where incorrect subtraction could lead to
  636. /// infinite loops or wrong calculations.
  637. ///
  638. /// Mutant testing: Catches mutations that replace the subtraction implementation
  639. /// with `Default::default()` (returning Amount::ZERO), which would cause infinite
  640. /// loops in `split_targeted` at line 138 where `*self - parts_total` is computed.
  641. #[test]
  642. fn test_amount_sub_operator() {
  643. let amount1 = Amount::from(100);
  644. let amount2 = Amount::from(30);
  645. let result = amount1 - amount2;
  646. assert_eq!(result, Amount::from(70));
  647. let amount1 = Amount::from(1000);
  648. let amount2 = Amount::from(1);
  649. let result = amount1 - amount2;
  650. assert_eq!(result, Amount::from(999));
  651. let amount1 = Amount::from(255);
  652. let amount2 = Amount::from(128);
  653. let result = amount1 - amount2;
  654. assert_eq!(result, Amount::from(127));
  655. }
  656. /// Tests that the subtraction operator panics when attempting to subtract
  657. /// a larger amount from a smaller amount (underflow).
  658. ///
  659. /// This test verifies the safety property that Amount subtraction will panic
  660. /// rather than wrap around on underflow. This is critical for preventing
  661. /// bugs where negative amounts could be interpreted as very large positive amounts.
  662. ///
  663. /// Mutant testing: Catches mutations that remove the panic behavior or return
  664. /// default values instead of properly handling underflow.
  665. #[test]
  666. #[should_panic(expected = "Subtraction underflow")]
  667. fn test_amount_sub_underflow() {
  668. let amount1 = Amount::from(30);
  669. let amount2 = Amount::from(100);
  670. let _result = amount1 - amount2;
  671. }
  672. /// Tests that checked_add correctly computes the sum and returns the actual value.
  673. ///
  674. /// This is critical because checked_add is used in recursive functions like
  675. /// split_with_fee. If it returns Some(Amount::ZERO) instead of the actual sum,
  676. /// the recursion would never terminate.
  677. ///
  678. /// Mutant testing: Kills mutations that replace the implementation with
  679. /// `Some(Default::default())`, which would cause infinite loops in split_with_fee
  680. /// at line 198 where it recursively calls itself with incremented amounts.
  681. #[test]
  682. fn test_checked_add_returns_correct_value() {
  683. let amount1 = Amount::from(100);
  684. let amount2 = Amount::from(50);
  685. let result = amount1.checked_add(amount2);
  686. assert_eq!(result, Some(Amount::from(150)));
  687. let amount1 = Amount::from(1);
  688. let amount2 = Amount::from(1);
  689. let result = amount1.checked_add(amount2);
  690. assert_eq!(result, Some(Amount::from(2)));
  691. assert_ne!(result, Some(Amount::ZERO));
  692. let amount1 = Amount::from(1000);
  693. let amount2 = Amount::from(337);
  694. let result = amount1.checked_add(amount2);
  695. assert_eq!(result, Some(Amount::from(1337)));
  696. }
  697. /// Tests that checked_add returns None on overflow.
  698. #[test]
  699. fn test_checked_add_overflow() {
  700. let amount1 = Amount::from(u64::MAX);
  701. let amount2 = Amount::from(1);
  702. let result = amount1.checked_add(amount2);
  703. assert!(result.is_none());
  704. }
  705. /// Tests that try_sum correctly computes the total sum of amounts.
  706. ///
  707. /// This is critical because try_sum is used in loops like split_targeted at line 130
  708. /// to track progress. If it returns Ok(Amount::ZERO) instead of the actual sum,
  709. /// the loop condition `parts_total.eq(self)` would never be true, causing an infinite loop.
  710. ///
  711. /// Mutant testing: Kills mutations that replace the implementation with
  712. /// `Ok(Default::default())`, which would cause infinite loops.
  713. #[test]
  714. fn test_try_sum_returns_correct_value() {
  715. let amounts = vec![Amount::from(10), Amount::from(20), Amount::from(30)];
  716. let result = Amount::try_sum(amounts).unwrap();
  717. assert_eq!(result, Amount::from(60));
  718. assert_ne!(result, Amount::ZERO);
  719. let amounts = vec![Amount::from(1), Amount::from(1), Amount::from(1)];
  720. let result = Amount::try_sum(amounts).unwrap();
  721. assert_eq!(result, Amount::from(3));
  722. let amounts = vec![Amount::from(100)];
  723. let result = Amount::try_sum(amounts).unwrap();
  724. assert_eq!(result, Amount::from(100));
  725. let empty: Vec<Amount> = vec![];
  726. let result = Amount::try_sum(empty).unwrap();
  727. assert_eq!(result, Amount::ZERO);
  728. }
  729. /// Tests that try_sum returns error on overflow.
  730. #[test]
  731. fn test_try_sum_overflow() {
  732. let amounts = vec![Amount::from(u64::MAX), Amount::from(1)];
  733. let result = Amount::try_sum(amounts);
  734. assert!(result.is_err());
  735. }
  736. /// Tests that split returns a non-empty vec with actual values, not defaults.
  737. ///
  738. /// The split function is used in split_targeted's while loop (line 122).
  739. /// If split returns an empty vec or vec with Amount::ZERO when it shouldn't,
  740. /// the loop that extends parts with split results would never make progress,
  741. /// causing an infinite loop.
  742. ///
  743. /// Mutant testing: Kills mutations that replace split with `vec![]` or
  744. /// `vec![Default::default()]` which would cause infinite loops.
  745. #[test]
  746. fn test_split_returns_correct_values() {
  747. let fee_and_amounts = (0, (0..32).map(|x| 2u64.pow(x)).collect::<Vec<_>>()).into();
  748. let amount = Amount::from(11);
  749. let result = amount.split(&fee_and_amounts);
  750. assert!(!result.is_empty());
  751. assert_eq!(Amount::try_sum(result.iter().copied()).unwrap(), amount);
  752. let amount = Amount::from(255);
  753. let result = amount.split(&fee_and_amounts);
  754. assert!(!result.is_empty());
  755. assert_eq!(Amount::try_sum(result.iter().copied()).unwrap(), amount);
  756. let amount = Amount::from(7);
  757. let result = amount.split(&fee_and_amounts);
  758. assert_eq!(
  759. result,
  760. vec![Amount::from(4), Amount::from(2), Amount::from(1)]
  761. );
  762. for r in &result {
  763. assert_ne!(*r, Amount::ZERO);
  764. }
  765. }
  766. /// Tests that the modulo operation in split works correctly.
  767. ///
  768. /// At line 108, split uses modulo (%) to compute the remainder.
  769. /// If this is mutated to division (/), it would produce wrong results
  770. /// that could cause infinite loops in code that depends on split.
  771. ///
  772. /// Mutant testing: Kills mutations that replace `%` with `/`.
  773. #[test]
  774. fn test_split_modulo_operation() {
  775. let fee_and_amounts = (0, (0..32).map(|x| 2u64.pow(x)).collect::<Vec<_>>()).into();
  776. let amount = Amount::from(15);
  777. let result = amount.split(&fee_and_amounts);
  778. assert_eq!(
  779. result,
  780. vec![
  781. Amount::from(8),
  782. Amount::from(4),
  783. Amount::from(2),
  784. Amount::from(1)
  785. ]
  786. );
  787. let total = Amount::try_sum(result.iter().copied()).unwrap();
  788. assert_eq!(total, amount);
  789. }
  790. /// Tests that From<u64> correctly converts values to Amount.
  791. ///
  792. /// This conversion is used throughout the codebase including in loops and split operations.
  793. /// If it returns Default::default() (Amount::ZERO) instead of the actual value,
  794. /// it can cause infinite loops where amounts are being accumulated or compared.
  795. ///
  796. /// Mutant testing: Kills mutations that replace From<u64> with `Default::default()`.
  797. #[test]
  798. fn test_from_u64_returns_correct_value() {
  799. let amount = Amount::from(100u64);
  800. assert_eq!(amount, Amount(100));
  801. assert_ne!(amount, Amount::ZERO);
  802. let amount = Amount::from(1u64);
  803. assert_eq!(amount, Amount(1));
  804. assert_eq!(amount, Amount::ONE);
  805. let amount = Amount::from(1337u64);
  806. assert_eq!(amount.to_u64(), 1337);
  807. }
  808. /// Tests that checked_mul returns the correct product value.
  809. ///
  810. /// This is critical for any multiplication operations. If it returns None
  811. /// or Some(Amount::ZERO) instead of the actual product, calculations will be wrong.
  812. ///
  813. /// Mutant testing: Kills mutations that replace checked_mul with None or Some(Default::default()).
  814. #[test]
  815. fn test_checked_mul_returns_correct_value() {
  816. let amount1 = Amount::from(10);
  817. let amount2 = Amount::from(5);
  818. let result = amount1.checked_mul(amount2);
  819. assert_eq!(result, Some(Amount::from(50)));
  820. assert_ne!(result, None);
  821. assert_ne!(result, Some(Amount::ZERO));
  822. let amount1 = Amount::from(100);
  823. let amount2 = Amount::from(20);
  824. let result = amount1.checked_mul(amount2);
  825. assert_eq!(result, Some(Amount::from(2000)));
  826. assert_ne!(result, Some(Amount::ZERO));
  827. let amount1 = Amount::from(7);
  828. let amount2 = Amount::from(13);
  829. let result = amount1.checked_mul(amount2);
  830. assert_eq!(result, Some(Amount::from(91)));
  831. // Test multiplication by zero
  832. let amount1 = Amount::from(100);
  833. let amount2 = Amount::ZERO;
  834. let result = amount1.checked_mul(amount2);
  835. assert_eq!(result, Some(Amount::ZERO));
  836. // Test multiplication by one
  837. let amount1 = Amount::from(42);
  838. let amount2 = Amount::ONE;
  839. let result = amount1.checked_mul(amount2);
  840. assert_eq!(result, Some(Amount::from(42)));
  841. // Test overflow
  842. let amount1 = Amount::from(u64::MAX);
  843. let amount2 = Amount::from(2);
  844. let result = amount1.checked_mul(amount2);
  845. assert!(result.is_none());
  846. }
  847. /// Tests that checked_div returns the correct quotient value.
  848. ///
  849. /// This is critical for division operations. If it returns None or
  850. /// Some(Amount::ZERO) instead of the actual quotient, calculations will be wrong.
  851. ///
  852. /// Mutant testing: Kills mutations that replace checked_div with None or Some(Default::default()).
  853. #[test]
  854. fn test_checked_div_returns_correct_value() {
  855. let amount1 = Amount::from(100);
  856. let amount2 = Amount::from(5);
  857. let result = amount1.checked_div(amount2);
  858. assert_eq!(result, Some(Amount::from(20)));
  859. assert_ne!(result, None);
  860. assert_ne!(result, Some(Amount::ZERO));
  861. let amount1 = Amount::from(1000);
  862. let amount2 = Amount::from(10);
  863. let result = amount1.checked_div(amount2);
  864. assert_eq!(result, Some(Amount::from(100)));
  865. assert_ne!(result, Some(Amount::ZERO));
  866. let amount1 = Amount::from(91);
  867. let amount2 = Amount::from(7);
  868. let result = amount1.checked_div(amount2);
  869. assert_eq!(result, Some(Amount::from(13)));
  870. // Test division by one
  871. let amount1 = Amount::from(42);
  872. let amount2 = Amount::ONE;
  873. let result = amount1.checked_div(amount2);
  874. assert_eq!(result, Some(Amount::from(42)));
  875. // Test integer division (truncation)
  876. let amount1 = Amount::from(10);
  877. let amount2 = Amount::from(3);
  878. let result = amount1.checked_div(amount2);
  879. assert_eq!(result, Some(Amount::from(3)));
  880. // Test division by zero
  881. let amount1 = Amount::from(100);
  882. let amount2 = Amount::ZERO;
  883. let result = amount1.checked_div(amount2);
  884. assert!(result.is_none());
  885. }
  886. /// Tests that Amount::convert_unit returns the correct converted value.
  887. ///
  888. /// This is critical for unit conversions. If it returns Ok(Amount::ZERO)
  889. /// instead of the actual converted value, all conversions will be wrong.
  890. ///
  891. /// Mutant testing: Kills mutations that replace convert_unit with Ok(Default::default()).
  892. #[test]
  893. fn test_convert_unit_returns_correct_value() {
  894. let amount = Amount::from(1000);
  895. let result = amount
  896. .convert_unit(&CurrencyUnit::Sat, &CurrencyUnit::Msat)
  897. .unwrap();
  898. assert_eq!(result, Amount::from(1_000_000));
  899. assert_ne!(result, Amount::ZERO);
  900. let amount = Amount::from(5000);
  901. let result = amount
  902. .convert_unit(&CurrencyUnit::Msat, &CurrencyUnit::Sat)
  903. .unwrap();
  904. assert_eq!(result, Amount::from(5));
  905. assert_ne!(result, Amount::ZERO);
  906. let amount = Amount::from(123);
  907. let result = amount
  908. .convert_unit(&CurrencyUnit::Sat, &CurrencyUnit::Sat)
  909. .unwrap();
  910. assert_eq!(result, Amount::from(123));
  911. let amount = Amount::from(456);
  912. let result = amount
  913. .convert_unit(&CurrencyUnit::Usd, &CurrencyUnit::Usd)
  914. .unwrap();
  915. assert_eq!(result, Amount::from(456));
  916. let amount = Amount::from(789);
  917. let result = amount
  918. .convert_unit(&CurrencyUnit::Eur, &CurrencyUnit::Eur)
  919. .unwrap();
  920. assert_eq!(result, Amount::from(789));
  921. // Test invalid conversion
  922. let amount = Amount::from(100);
  923. let result = amount.convert_unit(&CurrencyUnit::Sat, &CurrencyUnit::Eur);
  924. assert!(result.is_err());
  925. }
  926. }