1use std::default;
18use std::error;
19use std::fmt::{self, Write};
20use std::ops;
21use std::str::FromStr;
22use std::cmp::Ordering;
23
24#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
26pub enum Denomination {
27 Bitcoin,
29 MilliBitcoin,
31 MicroBitcoin,
33 Bit,
35 Satoshi,
37 MilliSatoshi,
39}
40
41impl Denomination {
42 fn precision(self) -> i32 {
44 match self {
45 Denomination::Bitcoin => -8,
46 Denomination::MilliBitcoin => -5,
47 Denomination::MicroBitcoin => -2,
48 Denomination::Bit => -2,
49 Denomination::Satoshi => 0,
50 Denomination::MilliSatoshi => 3,
51 }
52 }
53}
54
55impl fmt::Display for Denomination {
56 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
57 f.write_str(match *self {
58 Denomination::Bitcoin => "BTC",
59 Denomination::MilliBitcoin => "mBTC",
60 Denomination::MicroBitcoin => "uBTC",
61 Denomination::Bit => "bits",
62 Denomination::Satoshi => "satoshi",
63 Denomination::MilliSatoshi => "msat",
64 })
65 }
66}
67
68impl FromStr for Denomination {
69 type Err = ParseAmountError;
70
71 fn from_str(s: &str) -> Result<Self, Self::Err> {
72 match s {
73 "BTC" => Ok(Denomination::Bitcoin),
74 "mBTC" => Ok(Denomination::MilliBitcoin),
75 "uBTC" => Ok(Denomination::MicroBitcoin),
76 "bits" => Ok(Denomination::Bit),
77 "satoshi" => Ok(Denomination::Satoshi),
78 "sat" => Ok(Denomination::Satoshi),
79 "msat" => Ok(Denomination::MilliSatoshi),
80 d => Err(ParseAmountError::UnknownDenomination(d.to_owned())),
81 }
82 }
83}
84
85#[derive(Debug, Clone, PartialEq, Eq)]
87pub enum ParseAmountError {
88 Negative,
90 TooBig,
92 TooPrecise,
94 InvalidFormat,
96 InputTooLarge,
98 InvalidCharacter(char),
100 UnknownDenomination(String),
102}
103
104impl fmt::Display for ParseAmountError {
105 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
106 match *self {
107 ParseAmountError::Negative => f.write_str("amount is negative"),
108 ParseAmountError::TooBig => f.write_str("amount is too big"),
109 ParseAmountError::TooPrecise => f.write_str("amount has a too high precision"),
110 ParseAmountError::InvalidFormat => f.write_str("invalid number format"),
111 ParseAmountError::InputTooLarge => f.write_str("input string was too large"),
112 ParseAmountError::InvalidCharacter(c) => write!(f, "invalid character in input: {}", c),
113 ParseAmountError::UnknownDenomination(ref d) => write!(f, "unknown denomination: {}",d),
114 }
115 }
116}
117
118impl error::Error for ParseAmountError {}
119
120fn is_too_precise(s: &str, precision: usize) -> bool {
121 s.contains('.') || precision >= s.len() || s.chars().rev().take(precision).any(|d| d != '0')
122}
123
124fn parse_signed_to_satoshi(
127 mut s: &str,
128 denom: Denomination,
129) -> Result<(bool, u64), ParseAmountError> {
130 if s.is_empty() {
131 return Err(ParseAmountError::InvalidFormat);
132 }
133 if s.len() > 50 {
134 return Err(ParseAmountError::InputTooLarge);
135 }
136
137 let is_negative = s.starts_with('-');
138 if is_negative {
139 if s.len() == 1 {
140 return Err(ParseAmountError::InvalidFormat);
141 }
142 s = &s[1..];
143 }
144
145 let max_decimals = {
146 let precision_diff = -denom.precision();
149 if precision_diff < 0 {
150 let last_n = precision_diff.abs() as usize;
155 if is_too_precise(s, last_n) {
156 return Err(ParseAmountError::TooPrecise);
157 }
158 s = &s[0..s.len() - last_n];
159 0
160 } else {
161 precision_diff
162 }
163 };
164
165 let mut decimals = None;
166 let mut value: u64 = 0; for c in s.chars() {
168 match c {
169 '0'..='9' => {
170 match 10_u64.checked_mul(value) {
172 None => return Err(ParseAmountError::TooBig),
173 Some(val) => match val.checked_add((c as u8 - b'0') as u64) {
174 None => return Err(ParseAmountError::TooBig),
175 Some(val) => value = val,
176 },
177 }
178 decimals = match decimals {
180 None => None,
181 Some(d) if d < max_decimals => Some(d + 1),
182 _ => return Err(ParseAmountError::TooPrecise),
183 };
184 }
185 '.' => match decimals {
186 None => decimals = Some(0),
187 _ => return Err(ParseAmountError::InvalidFormat),
189 },
190 c => return Err(ParseAmountError::InvalidCharacter(c)),
191 }
192 }
193
194 let scale_factor = max_decimals - decimals.unwrap_or(0);
196 for _ in 0..scale_factor {
197 value = match 10_u64.checked_mul(value) {
198 Some(v) => v,
199 None => return Err(ParseAmountError::TooBig),
200 };
201 }
202
203 Ok((is_negative, value))
204}
205
206fn fmt_satoshi_in(
210 satoshi: u64,
211 negative: bool,
212 f: &mut dyn fmt::Write,
213 denom: Denomination,
214) -> fmt::Result {
215 if negative {
216 f.write_str("-")?;
217 }
218
219 let precision = denom.precision();
220 match precision.cmp(&0) {
221 Ordering::Greater => {
222 let width = precision as usize;
224 write!(f, "{}{:0width$}", satoshi, 0, width = width)?;
225 }
226 Ordering::Less => {
227 let nb_decimals = precision.abs() as usize;
229 let real = format!("{:0width$}", satoshi, width = nb_decimals);
230 if real.len() == nb_decimals {
231 write!(f, "0.{}", &real[real.len() - nb_decimals..])?;
232 } else {
233 write!(
234 f,
235 "{}.{}",
236 &real[0..(real.len() - nb_decimals)],
237 &real[real.len() - nb_decimals..]
238 )?;
239 }
240 }
241 Ordering::Equal => write!(f, "{}", satoshi)?,
242 }
243 Ok(())
244}
245
246#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
264pub struct Amount(u64);
265
266impl Amount {
267 pub const ZERO: Amount = Amount(0);
269 pub const ONE_SAT: Amount = Amount(1);
271 pub const ONE_BTC: Amount = Amount(100_000_000);
273
274 pub fn from_sat(satoshi: u64) -> Amount {
276 Amount(satoshi)
277 }
278
279 pub fn as_sat(self) -> u64 {
281 self.0
282 }
283
284 pub fn max_value() -> Amount {
286 Amount(u64::max_value())
287 }
288
289 pub fn min_value() -> Amount {
291 Amount(u64::min_value())
292 }
293
294 pub fn from_btc(btc: f64) -> Result<Amount, ParseAmountError> {
296 Amount::from_float_in(btc, Denomination::Bitcoin)
297 }
298
299 pub fn from_str_in(s: &str, denom: Denomination) -> Result<Amount, ParseAmountError> {
304 let (negative, satoshi) = parse_signed_to_satoshi(s, denom)?;
305 if negative {
306 return Err(ParseAmountError::Negative);
307 }
308 if satoshi > i64::max_value() as u64 {
309 return Err(ParseAmountError::TooBig);
310 }
311 Ok(Amount::from_sat(satoshi))
312 }
313
314 pub fn from_str_with_denomination(s: &str) -> Result<Amount, ParseAmountError> {
319 let mut split = s.splitn(3, ' ');
320 let amt_str = split.next().unwrap();
321 let denom_str = split.next().ok_or(ParseAmountError::InvalidFormat)?;
322 if split.next().is_some() {
323 return Err(ParseAmountError::InvalidFormat);
324 }
325
326 Ok(Amount::from_str_in(amt_str, denom_str.parse()?)?)
327 }
328
329 pub fn to_float_in(self, denom: Denomination) -> f64 {
333 f64::from_str(&self.to_string_in(denom)).unwrap()
334 }
335
336 pub fn as_btc(self) -> f64 {
342 self.to_float_in(Denomination::Bitcoin)
343 }
344
345 pub fn from_float_in(value: f64, denom: Denomination) -> Result<Amount, ParseAmountError> {
351 if value < 0.0 {
352 return Err(ParseAmountError::Negative);
353 }
354 Amount::from_str_in(&value.to_string(), denom)
357 }
358
359 pub fn fmt_value_in(self, f: &mut dyn fmt::Write, denom: Denomination) -> fmt::Result {
363 fmt_satoshi_in(self.as_sat(), false, f, denom)
364 }
365
366 pub fn to_string_in(self, denom: Denomination) -> String {
370 let mut buf = String::new();
371 self.fmt_value_in(&mut buf, denom).unwrap();
372 buf
373 }
374
375 pub fn to_string_with_denomination(self, denom: Denomination) -> String {
378 let mut buf = String::new();
379 self.fmt_value_in(&mut buf, denom).unwrap();
380 write!(buf, " {}", denom).unwrap();
381 buf
382 }
383
384 pub fn checked_add(self, rhs: Amount) -> Option<Amount> {
389 self.0.checked_add(rhs.0).map(Amount)
390 }
391
392 pub fn checked_sub(self, rhs: Amount) -> Option<Amount> {
395 self.0.checked_sub(rhs.0).map(Amount)
396 }
397
398 pub fn checked_mul(self, rhs: u64) -> Option<Amount> {
401 self.0.checked_mul(rhs).map(Amount)
402 }
403
404 pub fn checked_div(self, rhs: u64) -> Option<Amount> {
409 self.0.checked_div(rhs).map(Amount)
410 }
411
412 pub fn checked_rem(self, rhs: u64) -> Option<Amount> {
415 self.0.checked_rem(rhs).map(Amount)
416 }
417
418 pub fn to_signed(self) -> Result<SignedAmount, ParseAmountError> {
420 if self.as_sat() > SignedAmount::max_value().as_sat() as u64 {
421 Err(ParseAmountError::TooBig)
422 } else {
423 Ok(SignedAmount::from_sat(self.as_sat() as i64))
424 }
425 }
426}
427
428impl default::Default for Amount {
429 fn default() -> Self {
430 Amount::ZERO
431 }
432}
433
434impl fmt::Debug for Amount {
435 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
436 write!(f, "Amount({} satoshi)", self.as_sat())
437 }
438}
439
440impl fmt::Display for Amount {
443 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
444 self.fmt_value_in(f, Denomination::Bitcoin)?;
445 write!(f, " {}", Denomination::Bitcoin)
446 }
447}
448
449impl ops::Add for Amount {
450 type Output = Amount;
451
452 fn add(self, rhs: Amount) -> Self::Output {
453 self.checked_add(rhs).expect("Amount addition error")
454 }
455}
456
457impl ops::AddAssign for Amount {
458 fn add_assign(&mut self, other: Amount) {
459 *self = *self + other
460 }
461}
462
463impl ops::Sub for Amount {
464 type Output = Amount;
465
466 fn sub(self, rhs: Amount) -> Self::Output {
467 self.checked_sub(rhs).expect("Amount subtraction error")
468 }
469}
470
471impl ops::SubAssign for Amount {
472 fn sub_assign(&mut self, other: Amount) {
473 *self = *self - other
474 }
475}
476
477impl ops::Rem<u64> for Amount {
478 type Output = Amount;
479
480 fn rem(self, modulus: u64) -> Self {
481 self.checked_rem(modulus).expect("Amount remainder error")
482 }
483}
484
485impl ops::RemAssign<u64> for Amount {
486 fn rem_assign(&mut self, modulus: u64) {
487 *self = *self % modulus
488 }
489}
490
491impl ops::Mul<u64> for Amount {
492 type Output = Amount;
493
494 fn mul(self, rhs: u64) -> Self::Output {
495 self.checked_mul(rhs).expect("Amount multiplication error")
496 }
497}
498
499impl ops::MulAssign<u64> for Amount {
500 fn mul_assign(&mut self, rhs: u64) {
501 *self = *self * rhs
502 }
503}
504
505impl ops::Div<u64> for Amount {
506 type Output = Amount;
507
508 fn div(self, rhs: u64) -> Self::Output {
509 self.checked_div(rhs).expect("Amount division error")
510 }
511}
512
513impl ops::DivAssign<u64> for Amount {
514 fn div_assign(&mut self, rhs: u64) {
515 *self = *self / rhs
516 }
517}
518
519impl FromStr for Amount {
520 type Err = ParseAmountError;
521
522 fn from_str(s: &str) -> Result<Self, Self::Err> {
523 Amount::from_str_with_denomination(s)
524 }
525}
526
527#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
542pub struct SignedAmount(i64);
543
544impl SignedAmount {
545 pub const ZERO: SignedAmount = SignedAmount(0);
547 pub const ONE_SAT: SignedAmount = SignedAmount(1);
549 pub const ONE_BTC: SignedAmount = SignedAmount(100_000_000);
551
552 pub fn from_sat(satoshi: i64) -> SignedAmount {
554 SignedAmount(satoshi)
555 }
556
557 pub fn as_sat(self) -> i64 {
559 self.0
560 }
561
562 pub fn max_value() -> SignedAmount {
564 SignedAmount(i64::max_value())
565 }
566
567 pub fn min_value() -> SignedAmount {
569 SignedAmount(i64::min_value())
570 }
571
572 pub fn from_btc(btc: f64) -> Result<SignedAmount, ParseAmountError> {
574 SignedAmount::from_float_in(btc, Denomination::Bitcoin)
575 }
576
577 pub fn from_str_in(s: &str, denom: Denomination) -> Result<SignedAmount, ParseAmountError> {
582 let (negative, satoshi) = parse_signed_to_satoshi(s, denom)?;
583 if satoshi > i64::max_value() as u64 {
584 return Err(ParseAmountError::TooBig);
585 }
586 Ok(match negative {
587 true => SignedAmount(-(satoshi as i64)),
588 false => SignedAmount(satoshi as i64),
589 })
590 }
591
592 pub fn from_str_with_denomination(s: &str) -> Result<SignedAmount, ParseAmountError> {
597 let mut split = s.splitn(3, ' ');
598 let amt_str = split.next().unwrap();
599 let denom_str = split.next().ok_or(ParseAmountError::InvalidFormat)?;
600 if split.next().is_some() {
601 return Err(ParseAmountError::InvalidFormat);
602 }
603
604 Ok(SignedAmount::from_str_in(amt_str, denom_str.parse()?)?)
605 }
606
607 pub fn to_float_in(self, denom: Denomination) -> f64 {
611 f64::from_str(&self.to_string_in(denom)).unwrap()
612 }
613
614 pub fn as_btc(self) -> f64 {
620 self.to_float_in(Denomination::Bitcoin)
621 }
622
623 pub fn from_float_in(
629 value: f64,
630 denom: Denomination,
631 ) -> Result<SignedAmount, ParseAmountError> {
632 SignedAmount::from_str_in(&value.to_string(), denom)
635 }
636
637 pub fn fmt_value_in(self, f: &mut dyn fmt::Write, denom: Denomination) -> fmt::Result {
641 let sats = self.as_sat().checked_abs().map(|a: i64| a as u64).unwrap_or_else(|| {
642 u64::max_value() - self.as_sat() as u64 +1
644 });
645 fmt_satoshi_in(sats, self.is_negative(), f, denom)
646 }
647
648 pub fn to_string_in(self, denom: Denomination) -> String {
652 let mut buf = String::new();
653 self.fmt_value_in(&mut buf, denom).unwrap();
654 buf
655 }
656
657 pub fn to_string_with_denomination(self, denom: Denomination) -> String {
660 let mut buf = String::new();
661 self.fmt_value_in(&mut buf, denom).unwrap();
662 write!(buf, " {}", denom).unwrap();
663 buf
664 }
665
666 pub fn abs(self) -> SignedAmount {
670 SignedAmount(self.0.abs())
671 }
672
673 pub fn signum(self) -> i64 {
679 self.0.signum()
680 }
681
682 pub fn is_positive(self) -> bool {
685 self.0.is_positive()
686 }
687
688 pub fn is_negative(self) -> bool {
691 self.0.is_negative()
692 }
693
694
695 pub fn checked_abs(self) -> Option<SignedAmount> {
698 self.0.checked_abs().map(SignedAmount)
699 }
700
701 pub fn checked_add(self, rhs: SignedAmount) -> Option<SignedAmount> {
704 self.0.checked_add(rhs.0).map(SignedAmount)
705 }
706
707 pub fn checked_sub(self, rhs: SignedAmount) -> Option<SignedAmount> {
710 self.0.checked_sub(rhs.0).map(SignedAmount)
711 }
712
713 pub fn checked_mul(self, rhs: i64) -> Option<SignedAmount> {
716 self.0.checked_mul(rhs).map(SignedAmount)
717 }
718
719 pub fn checked_div(self, rhs: i64) -> Option<SignedAmount> {
724 self.0.checked_div(rhs).map(SignedAmount)
725 }
726
727 pub fn checked_rem(self, rhs: i64) -> Option<SignedAmount> {
730 self.0.checked_rem(rhs).map(SignedAmount)
731 }
732
733 pub fn positive_sub(self, rhs: SignedAmount) -> Option<SignedAmount> {
736 if self.is_negative() || rhs.is_negative() || rhs > self {
737 None
738 } else {
739 self.checked_sub(rhs)
740 }
741 }
742
743 pub fn to_unsigned(self) -> Result<Amount, ParseAmountError> {
745 if self.is_negative() {
746 Err(ParseAmountError::Negative)
747 } else {
748 Ok(Amount::from_sat(self.as_sat() as u64))
749 }
750 }
751}
752
753impl default::Default for SignedAmount {
754 fn default() -> Self {
755 SignedAmount::ZERO
756 }
757}
758
759impl fmt::Debug for SignedAmount {
760 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
761 write!(f, "SignedAmount({} satoshi)", self.as_sat())
762 }
763}
764
765impl fmt::Display for SignedAmount {
768 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
769 self.fmt_value_in(f, Denomination::Bitcoin)?;
770 write!(f, " {}", Denomination::Bitcoin)
771 }
772}
773
774impl ops::Add for SignedAmount {
775 type Output = SignedAmount;
776
777 fn add(self, rhs: SignedAmount) -> Self::Output {
778 self.checked_add(rhs).expect("SignedAmount addition error")
779 }
780}
781
782impl ops::AddAssign for SignedAmount {
783 fn add_assign(&mut self, other: SignedAmount) {
784 *self = *self + other
785 }
786}
787
788impl ops::Sub for SignedAmount {
789 type Output = SignedAmount;
790
791 fn sub(self, rhs: SignedAmount) -> Self::Output {
792 self.checked_sub(rhs).expect("SignedAmount subtraction error")
793 }
794}
795
796impl ops::SubAssign for SignedAmount {
797 fn sub_assign(&mut self, other: SignedAmount) {
798 *self = *self - other
799 }
800}
801
802impl ops::Rem<i64> for SignedAmount {
803 type Output = SignedAmount;
804
805 fn rem(self, modulus: i64) -> Self {
806 self.checked_rem(modulus).expect("SignedAmount remainder error")
807 }
808}
809
810impl ops::RemAssign<i64> for SignedAmount {
811 fn rem_assign(&mut self, modulus: i64) {
812 *self = *self % modulus
813 }
814}
815
816impl ops::Mul<i64> for SignedAmount {
817 type Output = SignedAmount;
818
819 fn mul(self, rhs: i64) -> Self::Output {
820 self.checked_mul(rhs).expect("SignedAmount multiplication error")
821 }
822}
823
824impl ops::MulAssign<i64> for SignedAmount {
825 fn mul_assign(&mut self, rhs: i64) {
826 *self = *self * rhs
827 }
828}
829
830impl ops::Div<i64> for SignedAmount {
831 type Output = SignedAmount;
832
833 fn div(self, rhs: i64) -> Self::Output {
834 self.checked_div(rhs).expect("SignedAmount division error")
835 }
836}
837
838impl ops::DivAssign<i64> for SignedAmount {
839 fn div_assign(&mut self, rhs: i64) {
840 *self = *self / rhs
841 }
842}
843
844impl FromStr for SignedAmount {
845 type Err = ParseAmountError;
846
847 fn from_str(s: &str) -> Result<Self, Self::Err> {
848 SignedAmount::from_str_with_denomination(s)
849 }
850}
851
852#[cfg(feature = "serde")]
853pub mod serde {
854 #![allow(missing_docs)]
856
857 use serde::{Deserialize, Deserializer, Serialize, Serializer};
874 use util::amount::{Amount, Denomination, SignedAmount};
875
876 pub trait SerdeAmount: Copy + Sized {
879 fn ser_sat<S: Serializer>(self, s: S) -> Result<S::Ok, S::Error>;
880 fn des_sat<'d, D: Deserializer<'d>>(d: D) -> Result<Self, D::Error>;
881 fn ser_btc<S: Serializer>(self, s: S) -> Result<S::Ok, S::Error>;
882 fn des_btc<'d, D: Deserializer<'d>>(d: D) -> Result<Self, D::Error>;
883 }
884
885 impl SerdeAmount for Amount {
886 fn ser_sat<S: Serializer>(self, s: S) -> Result<S::Ok, S::Error> {
887 u64::serialize(&self.as_sat(), s)
888 }
889 fn des_sat<'d, D: Deserializer<'d>>(d: D) -> Result<Self, D::Error> {
890 Ok(Amount::from_sat(u64::deserialize(d)?))
891 }
892 fn ser_btc<S: Serializer>(self, s: S) -> Result<S::Ok, S::Error> {
893 f64::serialize(&self.to_float_in(Denomination::Bitcoin), s)
894 }
895 fn des_btc<'d, D: Deserializer<'d>>(d: D) -> Result<Self, D::Error> {
896 use serde::de::Error;
897 Ok(Amount::from_btc(f64::deserialize(d)?).map_err(D::Error::custom)?)
898 }
899 }
900
901 impl SerdeAmount for SignedAmount {
902 fn ser_sat<S: Serializer>(self, s: S) -> Result<S::Ok, S::Error> {
903 i64::serialize(&self.as_sat(), s)
904 }
905 fn des_sat<'d, D: Deserializer<'d>>(d: D) -> Result<Self, D::Error> {
906 Ok(SignedAmount::from_sat(i64::deserialize(d)?))
907 }
908 fn ser_btc<S: Serializer>(self, s: S) -> Result<S::Ok, S::Error> {
909 f64::serialize(&self.to_float_in(Denomination::Bitcoin), s)
910 }
911 fn des_btc<'d, D: Deserializer<'d>>(d: D) -> Result<Self, D::Error> {
912 use serde::de::Error;
913 Ok(SignedAmount::from_btc(f64::deserialize(d)?).map_err(D::Error::custom)?)
914 }
915 }
916
917 pub mod as_sat {
918 use serde::{Deserializer, Serializer};
922 use util::amount::serde::SerdeAmount;
923
924 pub fn serialize<A: SerdeAmount, S: Serializer>(a: &A, s: S) -> Result<S::Ok, S::Error> {
925 a.ser_sat(s)
926 }
927
928 pub fn deserialize<'d, A: SerdeAmount, D: Deserializer<'d>>(d: D) -> Result<A, D::Error> {
929 A::des_sat(d)
930 }
931
932 pub mod opt {
933 use serde::{Deserializer, Serializer};
937 use util::amount::serde::SerdeAmount;
938
939 pub fn serialize<A: SerdeAmount, S: Serializer>(
940 a: &Option<A>,
941 s: S,
942 ) -> Result<S::Ok, S::Error> {
943 match *a {
944 Some(a) => a.ser_sat(s),
945 None => s.serialize_none(),
946 }
947 }
948
949 pub fn deserialize<'d, A: SerdeAmount, D: Deserializer<'d>>(
950 d: D,
951 ) -> Result<Option<A>, D::Error> {
952 Ok(Some(A::des_sat(d)?))
953 }
954 }
955 }
956
957 pub mod as_btc {
958 use serde::{Deserializer, Serializer};
962 use util::amount::serde::SerdeAmount;
963
964 pub fn serialize<A: SerdeAmount, S: Serializer>(a: &A, s: S) -> Result<S::Ok, S::Error> {
965 a.ser_btc(s)
966 }
967
968 pub fn deserialize<'d, A: SerdeAmount, D: Deserializer<'d>>(d: D) -> Result<A, D::Error> {
969 A::des_btc(d)
970 }
971
972 pub mod opt {
973 use serde::{Deserializer, Serializer};
977 use util::amount::serde::SerdeAmount;
978
979 pub fn serialize<A: SerdeAmount, S: Serializer>(
980 a: &Option<A>,
981 s: S,
982 ) -> Result<S::Ok, S::Error> {
983 match *a {
984 Some(a) => a.ser_btc(s),
985 None => s.serialize_none(),
986 }
987 }
988
989 pub fn deserialize<'d, A: SerdeAmount, D: Deserializer<'d>>(
990 d: D,
991 ) -> Result<Option<A>, D::Error> {
992 Ok(Some(A::des_btc(d)?))
993 }
994 }
995 }
996}
997
998#[cfg(test)]
999mod tests {
1000 use super::*;
1001 use std::panic;
1002 use std::str::FromStr;
1003
1004 #[cfg(feature = "serde")]
1005 use serde_test;
1006
1007 #[test]
1008 fn add_sub_mul_div() {
1009 let sat = Amount::from_sat;
1010 let ssat = SignedAmount::from_sat;
1011
1012 assert_eq!(sat(15) + sat(15), sat(30));
1013 assert_eq!(sat(15) - sat(15), sat(0));
1014 assert_eq!(sat(14) * 3, sat(42));
1015 assert_eq!(sat(14) / 2, sat(7));
1016 assert_eq!(sat(14) % 3, sat(2));
1017 assert_eq!(ssat(15) - ssat(20), ssat(-5));
1018 assert_eq!(ssat(-14) * 3, ssat(-42));
1019 assert_eq!(ssat(-14) / 2, ssat(-7));
1020 assert_eq!(ssat(-14) % 3, ssat(-2));
1021
1022 let mut b = ssat(-5);
1023 b += ssat(13);
1024 assert_eq!(b, ssat(8));
1025 b -= ssat(3);
1026 assert_eq!(b, ssat(5));
1027 b *= 6;
1028 assert_eq!(b, ssat(30));
1029 b /= 3;
1030 assert_eq!(b, ssat(10));
1031 b %= 3;
1032 assert_eq!(b, ssat(1));
1033
1034 let result = panic::catch_unwind(|| Amount::max_value() + Amount::from_sat(1));
1036 assert!(result.is_err());
1037 let result = panic::catch_unwind(|| Amount::from_sat(8446744073709551615) * 3);
1038 assert!(result.is_err());
1039 }
1040
1041 #[test]
1042 fn checked_arithmetic() {
1043 let sat = Amount::from_sat;
1044 let ssat = SignedAmount::from_sat;
1045
1046 assert_eq!(sat(42).checked_add(sat(1)), Some(sat(43)));
1047 assert_eq!(SignedAmount::max_value().checked_add(ssat(1)), None);
1048 assert_eq!(SignedAmount::min_value().checked_sub(ssat(1)), None);
1049 assert_eq!(Amount::max_value().checked_add(sat(1)), None);
1050 assert_eq!(Amount::min_value().checked_sub(sat(1)), None);
1051
1052 assert_eq!(sat(5).checked_sub(sat(3)), Some(sat(2)));
1053 assert_eq!(sat(5).checked_sub(sat(6)), None);
1054 assert_eq!(ssat(5).checked_sub(ssat(6)), Some(ssat(-1)));
1055 assert_eq!(sat(5).checked_rem(2), Some(sat(1)));
1056
1057 assert_eq!(sat(5).checked_div(2), Some(sat(2))); assert_eq!(ssat(-6).checked_div(2), Some(ssat(-3)));
1059
1060 assert_eq!(ssat(-5).positive_sub(ssat(3)), None);
1061 assert_eq!(ssat(5).positive_sub(ssat(-3)), None);
1062 assert_eq!(ssat(3).positive_sub(ssat(5)), None);
1063 assert_eq!(ssat(3).positive_sub(ssat(3)), Some(ssat(0)));
1064 assert_eq!(ssat(5).positive_sub(ssat(3)), Some(ssat(2)));
1065 }
1066
1067 #[test]
1068 fn floating_point() {
1069 use super::Denomination as D;
1070 let f = Amount::from_float_in;
1071 let sf = SignedAmount::from_float_in;
1072 let sat = Amount::from_sat;
1073 let ssat = SignedAmount::from_sat;
1074
1075 assert_eq!(f(11.22, D::Bitcoin), Ok(sat(1122000000)));
1076 assert_eq!(sf(-11.22, D::MilliBitcoin), Ok(ssat(-1122000)));
1077 assert_eq!(f(11.22, D::Bit), Ok(sat(1122)));
1078 assert_eq!(sf(-1000.0, D::MilliSatoshi), Ok(ssat(-1)));
1079 assert_eq!(f(0.0001234, D::Bitcoin), Ok(sat(12340)));
1080 assert_eq!(sf(-0.00012345, D::Bitcoin), Ok(ssat(-12345)));
1081
1082 assert_eq!(f(-100.0, D::MilliSatoshi), Err(ParseAmountError::Negative));
1083 assert_eq!(f(11.22, D::Satoshi), Err(ParseAmountError::TooPrecise));
1084 assert_eq!(sf(-100.0, D::MilliSatoshi), Err(ParseAmountError::TooPrecise));
1085 assert_eq!(sf(-100.0, D::MilliSatoshi), Err(ParseAmountError::TooPrecise));
1086 assert_eq!(f(42.123456781, D::Bitcoin), Err(ParseAmountError::TooPrecise));
1087 assert_eq!(sf(-184467440738.0, D::Bitcoin), Err(ParseAmountError::TooBig));
1088 assert_eq!(f(18446744073709551617.0, D::Satoshi), Err(ParseAmountError::TooBig));
1089 assert_eq!(
1090 f(SignedAmount::max_value().to_float_in(D::Satoshi) + 1.0, D::Satoshi),
1091 Err(ParseAmountError::TooBig)
1092 );
1093 assert_eq!(
1094 f(Amount::max_value().to_float_in(D::Satoshi) + 1.0, D::Satoshi),
1095 Err(ParseAmountError::TooBig)
1096 );
1097
1098 let btc = move |f| SignedAmount::from_btc(f).unwrap();
1099 assert_eq!(btc(2.5).to_float_in(D::Bitcoin), 2.5);
1100 assert_eq!(btc(-2.5).to_float_in(D::MilliBitcoin), -2500.0);
1101 assert_eq!(btc(2.5).to_float_in(D::Satoshi), 250000000.0);
1102 assert_eq!(btc(-2.5).to_float_in(D::MilliSatoshi), -250000000000.0);
1103
1104 let btc = move |f| Amount::from_btc(f).unwrap();
1105 assert_eq!(&btc(0.0012).to_float_in(D::Bitcoin).to_string(), "0.0012")
1106 }
1107
1108 #[test]
1109 fn parsing() {
1110 use super::ParseAmountError as E;
1111 let btc = Denomination::Bitcoin;
1112 let sat = Denomination::Satoshi;
1113 let p = Amount::from_str_in;
1114 let sp = SignedAmount::from_str_in;
1115
1116 assert_eq!(p("x", btc), Err(E::InvalidCharacter('x')));
1117 assert_eq!(p("-", btc), Err(E::InvalidFormat));
1118 assert_eq!(sp("-", btc), Err(E::InvalidFormat));
1119 assert_eq!(p("-1.0x", btc), Err(E::InvalidCharacter('x')));
1120 assert_eq!(p("0.0 ", btc), Err(ParseAmountError::InvalidCharacter(' ')));
1121 assert_eq!(p("0.000.000", btc), Err(E::InvalidFormat));
1122 let more_than_max = format!("1{}", Amount::max_value());
1123 assert_eq!(p(&more_than_max, btc), Err(E::TooBig));
1124 assert_eq!(p("0.000000042", btc), Err(E::TooPrecise));
1125
1126 assert_eq!(p("1", btc), Ok(Amount::from_sat(1_000_000_00)));
1127 assert_eq!(sp("-.5", btc), Ok(SignedAmount::from_sat(-500_000_00)));
1128 assert_eq!(p("1.1", btc), Ok(Amount::from_sat(1_100_000_00)));
1129 assert_eq!(p("100", sat), Ok(Amount::from_sat(100)));
1130 assert_eq!(p("55", sat), Ok(Amount::from_sat(55)));
1131 assert_eq!(p("5500000000000000000", sat), Ok(Amount::from_sat(5_500_000_000_000_000_000)));
1132 assert_eq!(p("5500000000000000000.", sat), Ok(Amount::from_sat(5_500_000_000_000_000_000)));
1134 assert_eq!(
1135 p("12345678901.12345678", btc),
1136 Ok(Amount::from_sat(12_345_678_901__123_456_78))
1137 );
1138
1139 let amount = Amount::from_sat(i64::max_value() as u64);
1141 assert_eq!(Amount::from_str_in(&amount.to_string_in(sat), sat), Ok(amount));
1142 assert_eq!(Amount::from_str_in(&(amount+Amount(1)).to_string_in(sat), sat), Err(E::TooBig));
1143
1144 assert_eq!(p("12.000", Denomination::MilliSatoshi), Err(E::TooPrecise));
1145 assert_eq!(p("100000000000000.0000000000000000000000000000000000", Denomination::Bitcoin), Err(E::TooBig));
1147 assert_eq!(p("100000000000000.00000000000000000000000000000000000", Denomination::Bitcoin), Err(E::InputTooLarge));
1149 }
1150
1151 #[test]
1152 fn to_string() {
1153 use super::Denomination as D;
1154
1155 assert_eq!(Amount::ONE_BTC.to_string_in(D::Bitcoin), "1.00000000");
1156 assert_eq!(Amount::ONE_BTC.to_string_in(D::Satoshi), "100000000");
1157 assert_eq!(Amount::ONE_SAT.to_string_in(D::Bitcoin), "0.00000001");
1158 assert_eq!(SignedAmount::from_sat(-42).to_string_in(D::Bitcoin), "-0.00000042");
1159
1160 assert_eq!(Amount::ONE_BTC.to_string_with_denomination(D::Bitcoin), "1.00000000 BTC");
1161 assert_eq!(Amount::ONE_SAT.to_string_with_denomination(D::MilliSatoshi), "1000 msat");
1162 assert_eq!(
1163 SignedAmount::ONE_BTC.to_string_with_denomination(D::Satoshi),
1164 "100000000 satoshi"
1165 );
1166 assert_eq!(Amount::ONE_SAT.to_string_with_denomination(D::Bitcoin), "0.00000001 BTC");
1167 assert_eq!(
1168 SignedAmount::from_sat(-42).to_string_with_denomination(D::Bitcoin),
1169 "-0.00000042 BTC"
1170 );
1171 }
1172
1173 #[test]
1174 fn test_unsigned_signed_conversion() {
1175 use super::ParseAmountError as E;
1176 let sa = SignedAmount::from_sat;
1177 let ua = Amount::from_sat;
1178
1179 assert_eq!(Amount::max_value().to_signed(), Err(E::TooBig));
1180 assert_eq!(ua(i64::max_value() as u64).to_signed(), Ok(sa(i64::max_value())));
1181 assert_eq!(ua(0).to_signed(), Ok(sa(0)));
1182 assert_eq!(ua(1).to_signed(), Ok( sa(1)));
1183 assert_eq!(ua(1).to_signed(), Ok(sa(1)));
1184 assert_eq!(ua(i64::max_value() as u64 + 1).to_signed(), Err(E::TooBig));
1185
1186 assert_eq!(sa(-1).to_unsigned(), Err(E::Negative));
1187 assert_eq!(sa(i64::max_value()).to_unsigned(), Ok(ua(i64::max_value() as u64)));
1188
1189 assert_eq!(sa(0).to_unsigned().unwrap().to_signed(), Ok(sa(0)));
1190 assert_eq!(sa(1).to_unsigned().unwrap().to_signed(), Ok(sa(1)));
1191 assert_eq!(sa(i64::max_value()).to_unsigned().unwrap().to_signed(), Ok(sa(i64::max_value())));
1192 }
1193
1194 #[test]
1195 fn from_str() {
1196 use super::ParseAmountError as E;
1197 let p = Amount::from_str;
1198 let sp = SignedAmount::from_str;
1199
1200 assert_eq!(p("x BTC"), Err(E::InvalidCharacter('x')));
1201 assert_eq!(p("5 BTC BTC"), Err(E::InvalidFormat));
1202 assert_eq!(p("5 5 BTC"), Err(E::InvalidFormat));
1203
1204 assert_eq!(p("5 BCH"), Err(E::UnknownDenomination("BCH".to_owned())));
1205
1206 assert_eq!(p("-1 BTC"), Err(E::Negative));
1207 assert_eq!(p("-0.0 BTC"), Err(E::Negative));
1208 assert_eq!(p("0.123456789 BTC"), Err(E::TooPrecise));
1209 assert_eq!(sp("-0.1 satoshi"), Err(E::TooPrecise));
1210 assert_eq!(p("0.123456 mBTC"), Err(E::TooPrecise));
1211 assert_eq!(sp("-1.001 bits"), Err(E::TooPrecise));
1212 assert_eq!(sp("-200000000000 BTC"), Err(E::TooBig));
1213 assert_eq!(p("18446744073709551616 sat"), Err(E::TooBig));
1214
1215 assert_eq!(sp("0 msat"), Err(E::TooPrecise));
1216 assert_eq!(sp("-0 msat"), Err(E::TooPrecise));
1217 assert_eq!(sp("000 msat"), Err(E::TooPrecise));
1218 assert_eq!(sp("-000 msat"), Err(E::TooPrecise));
1219 assert_eq!(p("0 msat"), Err(E::TooPrecise));
1220 assert_eq!(p("-0 msat"), Err(E::TooPrecise));
1221 assert_eq!(p("000 msat"), Err(E::TooPrecise));
1222 assert_eq!(p("-000 msat"), Err(E::TooPrecise));
1223
1224 assert_eq!(p(".5 bits"), Ok(Amount::from_sat(50)));
1225 assert_eq!(sp("-.5 bits"), Ok(SignedAmount::from_sat(-50)));
1226 assert_eq!(p("0.00253583 BTC"), Ok(Amount::from_sat(253583)));
1227 assert_eq!(sp("-5 satoshi"), Ok(SignedAmount::from_sat(-5)));
1228 assert_eq!(p("0.10000000 BTC"), Ok(Amount::from_sat(100_000_00)));
1229 assert_eq!(sp("-100 bits"), Ok(SignedAmount::from_sat(-10_000)));
1230 }
1231
1232 #[test]
1233 fn to_from_string_in() {
1234 use super::Denomination as D;
1235 let ua_str = Amount::from_str_in;
1236 let ua_sat = Amount::from_sat;
1237 let sa_str = SignedAmount::from_str_in;
1238 let sa_sat = SignedAmount::from_sat;
1239
1240 assert_eq!("0.50", Amount::from_sat(50).to_string_in(D::Bit));
1241 assert_eq!("-0.50", SignedAmount::from_sat(-50).to_string_in(D::Bit));
1242 assert_eq!("0.00253583", Amount::from_sat(253583).to_string_in(D::Bitcoin));
1243 assert_eq!("-5", SignedAmount::from_sat(-5).to_string_in(D::Satoshi));
1244 assert_eq!("0.10000000", Amount::from_sat(100_000_00).to_string_in(D::Bitcoin));
1245 assert_eq!("-100.00", SignedAmount::from_sat(-10_000).to_string_in(D::Bit));
1246
1247 assert_eq!(ua_str(&ua_sat(0).to_string_in(D::Satoshi), D::Satoshi), Ok(ua_sat(0)));
1248 assert_eq!(ua_str(&ua_sat(500).to_string_in(D::Bitcoin), D::Bitcoin), Ok(ua_sat(500)));
1249 assert_eq!(ua_str(&ua_sat(21_000_000).to_string_in(D::Bit), D::Bit), Ok(ua_sat(21_000_000)));
1250 assert_eq!(ua_str(&ua_sat(1).to_string_in(D::MicroBitcoin), D::MicroBitcoin), Ok(ua_sat(1)));
1251 assert_eq!(ua_str(&ua_sat(1_000_000_000_000).to_string_in(D::MilliBitcoin), D::MilliBitcoin), Ok(ua_sat(1_000_000_000_000)));
1252 assert_eq!(ua_str(&ua_sat(u64::max_value()).to_string_in(D::MilliBitcoin), D::MilliBitcoin), Err(ParseAmountError::TooBig));
1253
1254 assert_eq!(sa_str(&sa_sat(-1).to_string_in(D::MicroBitcoin), D::MicroBitcoin), Ok(sa_sat(-1)));
1255
1256 assert_eq!(sa_str(&sa_sat(i64::max_value()).to_string_in(D::Satoshi), D::MicroBitcoin), Err(ParseAmountError::TooBig));
1257 assert_eq!(sa_str(&sa_sat(i64::min_value()).to_string_in(D::Satoshi), D::MicroBitcoin), Err(ParseAmountError::TooBig));
1259
1260 }
1261
1262 #[test]
1263 fn to_string_with_denomination_from_str_roundtrip() {
1264 use super::Denomination as D;
1265 let amt = Amount::from_sat(42);
1266 let denom = Amount::to_string_with_denomination;
1267 assert_eq!(Amount::from_str(&denom(amt, D::Bitcoin)), Ok(amt));
1268 assert_eq!(Amount::from_str(&denom(amt, D::MilliBitcoin)), Ok(amt));
1269 assert_eq!(Amount::from_str(&denom(amt, D::MicroBitcoin)), Ok(amt));
1270 assert_eq!(Amount::from_str(&denom(amt, D::Bit)), Ok(amt));
1271 assert_eq!(Amount::from_str(&denom(amt, D::Satoshi)), Ok(amt));
1272 assert_eq!(Amount::from_str(&denom(amt, D::MilliSatoshi)), Ok(amt));
1273
1274 assert_eq!(Amount::from_str("42 satoshi BTC"), Err(ParseAmountError::InvalidFormat));
1275 assert_eq!(SignedAmount::from_str("-42 satoshi BTC"), Err(ParseAmountError::InvalidFormat));
1276 }
1277
1278 #[cfg(feature = "serde")]
1279 #[test]
1280 fn serde_as_sat() {
1281
1282 #[derive(Serialize, Deserialize, PartialEq, Debug)]
1283 struct T {
1284 #[serde(with = "::util::amount::serde::as_sat")]
1285 pub amt: Amount,
1286 #[serde(with = "::util::amount::serde::as_sat")]
1287 pub samt: SignedAmount,
1288 }
1289
1290 serde_test::assert_tokens(
1291 &T {
1292 amt: Amount::from_sat(123456789),
1293 samt: SignedAmount::from_sat(-123456789),
1294 },
1295 &[
1296 serde_test::Token::Struct {
1297 name: "T",
1298 len: 2,
1299 },
1300 serde_test::Token::Str("amt"),
1301 serde_test::Token::U64(123456789),
1302 serde_test::Token::Str("samt"),
1303 serde_test::Token::I64(-123456789),
1304 serde_test::Token::StructEnd,
1305 ],
1306 );
1307 }
1308
1309 #[cfg(feature = "serde")]
1310 #[test]
1311 fn serde_as_btc() {
1312 use serde_json;
1313
1314 #[derive(Serialize, Deserialize, PartialEq, Debug)]
1315 struct T {
1316 #[serde(with = "::util::amount::serde::as_btc")]
1317 pub amt: Amount,
1318 #[serde(with = "::util::amount::serde::as_btc")]
1319 pub samt: SignedAmount,
1320 }
1321
1322 let orig = T {
1323 amt: Amount::from_sat(21_000_000__000_000_01),
1324 samt: SignedAmount::from_sat(-21_000_000__000_000_01),
1325 };
1326
1327 let json = "{\"amt\": 21000000.00000001, \
1328 \"samt\": -21000000.00000001}";
1329 let t: T = serde_json::from_str(&json).unwrap();
1330 assert_eq!(t, orig);
1331
1332 let value: serde_json::Value = serde_json::from_str(&json).unwrap();
1333 assert_eq!(t, serde_json::from_value(value).unwrap());
1334
1335 let t: Result<T, serde_json::Error> =
1337 serde_json::from_str("{\"amt\": 1000000.000000001, \"samt\": 1}");
1338 assert!(t.unwrap_err().to_string().contains(&ParseAmountError::TooPrecise.to_string()));
1339 let t: Result<T, serde_json::Error> = serde_json::from_str("{\"amt\": -1, \"samt\": 1}");
1340 assert!(t.unwrap_err().to_string().contains(&ParseAmountError::Negative.to_string()));
1341 }
1342
1343 #[cfg(feature = "serde")]
1344 #[test]
1345 fn serde_as_btc_opt() {
1346 use serde_json;
1347
1348 #[derive(Serialize, Deserialize, PartialEq, Debug)]
1349 struct T {
1350 #[serde(default, with = "::util::amount::serde::as_btc::opt")]
1351 pub amt: Option<Amount>,
1352 #[serde(default, with = "::util::amount::serde::as_btc::opt")]
1353 pub samt: Option<SignedAmount>,
1354 }
1355
1356 let with = T {
1357 amt: Some(Amount::from_sat(2__500_000_00)),
1358 samt: Some(SignedAmount::from_sat(-2__500_000_00)),
1359 };
1360 let without = T {
1361 amt: None,
1362 samt: None,
1363 };
1364
1365 let t: T = serde_json::from_str("{\"amt\": 2.5, \"samt\": -2.5}").unwrap();
1366 assert_eq!(t, with);
1367
1368 let t: T = serde_json::from_str("{}").unwrap();
1369 assert_eq!(t, without);
1370
1371 let value_with: serde_json::Value =
1372 serde_json::from_str("{\"amt\": 2.5, \"samt\": -2.5}").unwrap();
1373 assert_eq!(with, serde_json::from_value(value_with).unwrap());
1374
1375 let value_without: serde_json::Value = serde_json::from_str("{}").unwrap();
1376 assert_eq!(without, serde_json::from_value(value_without).unwrap());
1377 }
1378}