use std::fmt;
use std::str::FromStr;
use std::convert::TryFrom;
const SATS_IN_BTC: u64 = 100_000_000;
const MAX_MONEY_SAT: u64 = 21_000_000 * SATS_IN_BTC;
const MAX_MONEY_MSAT: u64 = MAX_MONEY_SAT * 1000;
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Default)]
pub struct Amount(u64);
impl Amount {
pub const ZERO: Amount = Amount(0);
pub const ONE_MSAT: Amount = Amount(1);
pub const MAX: Amount = Amount(MAX_MONEY_MSAT);
pub const ONE_SAT: Amount = Amount(1000);
pub const ONE_BTC: Amount = Amount(1000 * SATS_IN_BTC);
#[inline]
pub fn from_msat(msat: u64) -> Result<Self, OverflowError> {
if msat > MAX_MONEY_MSAT {
Err(OverflowError { amount: msat, denomination: "millisatoshis", })
} else {
Ok(Amount(msat))
}
}
#[inline]
pub fn from_sat(sat: u64) -> Result<Self, OverflowError> {
if sat > MAX_MONEY_SAT {
Err(OverflowError { amount: sat, denomination: "satoshis", })
} else {
Ok(Amount(sat * 1000))
}
}
#[inline]
pub fn to_msat(self) -> u64 {
self.0
}
#[inline]
pub fn to_sat(self) -> Result<u64, FractionError> {
if self.0 % 1000 == 0 {
Ok(self.0 / 1000)
} else {
Err(FractionError { amount: self.0, })
}
}
#[inline]
pub fn to_sat_floor(self) -> u64 {
self.0 / 1000
}
#[inline]
pub fn to_sat_ceiling(self) -> u64 {
(self.0 + 999) / 1000
}
#[inline]
pub fn to_sat_round(self) -> u64 {
(self.0 + 500) / 1000
}
fn parse_raw(mut s: &str) -> Result<Self, ParseErrorInner> {
if s.ends_with(" msat") {
s = &s[..(s.len() - 5)];
}
let amount = s.parse::<u64>()?;
Self::from_msat(amount).map_err(Into::into)
}
#[inline]
fn internal_parse<S: AsRef<str> + Into<String>>(s: S) -> Result<Self, ParseError> {
Self::parse_raw(s.as_ref()).map_err(|error| ParseError {
input: s.into(),
reason: error,
})
}
}
impl fmt::Display for Amount {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{} msat", self.0)
}
}
impl fmt::Debug for Amount {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Display::fmt(self, f)
}
}
impl std::ops::Add for Amount {
type Output = Self;
#[inline]
fn add(self, rhs: Amount) -> Self::Output {
let sum = self.0 + rhs.0;
assert!(
sum <= MAX_MONEY_MSAT,
"adding amounts {} + {} overflowed the maximum number of 21 million bitcoins",
self,
rhs,
);
Amount(sum)
}
}
impl std::ops::AddAssign for Amount {
#[inline]
fn add_assign(&mut self, rhs: Amount) {
*self = *self + rhs;
}
}
impl std::ops::Sub for Amount {
type Output = Self;
#[inline]
fn sub(self, rhs: Amount) -> Self::Output {
Amount(self.0.checked_sub(rhs.0).expect("underflow when subtracting amounts"))
}
}
impl std::ops::SubAssign for Amount {
#[inline]
fn sub_assign(&mut self, rhs: Amount) {
*self = *self - rhs
}
}
impl std::ops::Mul<u64> for Amount {
type Output = Self;
fn mul(self, rhs: u64) -> Self::Output {
match self.0.checked_mul(rhs) {
Some(amount) if amount <= MAX_MONEY_MSAT => Amount(amount),
_ => panic!("multiplying {} by {} overflowed the maximum number of 21 million bitcoins", self, rhs),
}
}
}
impl std::ops::Mul<Amount> for u64 {
type Output = Amount;
fn mul(self, rhs: Amount) -> Self::Output {
rhs * self
}
}
impl std::ops::MulAssign<u64> for Amount {
fn mul_assign(&mut self, rhs: u64) {
*self = *self * rhs;
}
}
impl std::ops::Div<u64> for Amount {
type Output = Self;
fn div(self, rhs: u64) -> Self::Output {
Amount(self.0 / rhs)
}
}
impl std::ops::DivAssign<u64> for Amount {
fn div_assign(&mut self, rhs: u64) {
*self = *self / rhs;
}
}
impl std::ops::Rem<u64> for Amount {
type Output = Self;
fn rem(self, rhs: u64) -> Self::Output {
Amount(self.0 % rhs)
}
}
impl std::ops::RemAssign<u64> for Amount {
fn rem_assign(&mut self, rhs: u64) {
*self = *self % rhs;
}
}
impl FromStr for Amount {
type Err = ParseError;
#[inline]
fn from_str(s: &str) -> Result<Self, Self::Err> {
Self::internal_parse(s)
}
}
impl<'a> TryFrom<&'a str> for Amount {
type Error = ParseError;
#[inline]
fn try_from(s: &'a str) -> Result<Self, Self::Error> {
Self::internal_parse(s)
}
}
impl TryFrom<String> for Amount {
type Error = ParseError;
#[inline]
fn try_from(s: String) -> Result<Self, Self::Error> {
Self::internal_parse(s)
}
}
impl TryFrom<Box<str>> for Amount {
type Error = ParseError;
#[inline]
fn try_from(s: Box<str>) -> Result<Self, Self::Error> {
Self::internal_parse(s)
}
}
#[derive(Debug, Clone)]
pub struct ParseError {
input: String,
reason: ParseErrorInner,
}
impl fmt::Display for ParseError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "failed to parse '{}' as millisatoshis", self.input)
}
}
impl std::error::Error for ParseError {
#[inline]
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
Some(&self.reason)
}
}
#[derive(Debug, Clone)]
enum ParseErrorInner {
ParseInt(std::num::ParseIntError),
Overflow(OverflowError),
}
impl From<std::num::ParseIntError> for ParseErrorInner {
fn from(value: std::num::ParseIntError) -> Self {
ParseErrorInner::ParseInt(value)
}
}
impl From<OverflowError> for ParseErrorInner {
fn from(value: OverflowError) -> Self {
ParseErrorInner::Overflow(value)
}
}
impl fmt::Display for ParseErrorInner {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
ParseErrorInner::ParseInt(_) => f.write_str("invalid integer"),
ParseErrorInner::Overflow(_) => f.write_str("value above supply cap"),
}
}
}
impl std::error::Error for ParseErrorInner {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
ParseErrorInner::ParseInt(error) => Some(error),
ParseErrorInner::Overflow(error) => Some(error),
}
}
}
#[derive(Debug, Clone)]
pub struct OverflowError {
amount: u64,
denomination: &'static str,
}
impl fmt::Display for OverflowError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{} {} exceeds the maximum number of 21 million bitcoins", self.amount, self.denomination)
}
}
impl std::error::Error for OverflowError {}
#[derive(Debug, Clone)]
pub struct FractionError {
amount: u64,
}
impl fmt::Display for FractionError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{} millisatoshis can not be converted to satoshis because it's not round to thousands", self.amount)
}
}
impl std::error::Error for FractionError {}
#[cfg(feature = "bitcoin")]
mod impl_bitcoin {
use super::{Amount, OverflowError, FractionError};
use std::convert::TryFrom;
impl TryFrom<bitcoin::Amount> for Amount {
type Error = OverflowError;
fn try_from(value: bitcoin::Amount) -> Result<Self, Self::Error> {
Self::from_sat(value.as_sat())
}
}
impl TryFrom<Amount> for bitcoin::Amount {
type Error = FractionError;
fn try_from(value: Amount) -> Result<Self, Self::Error> {
Ok(Self::from_sat(value.to_sat()?))
}
}
}
#[cfg(feature = "parse_arg")]
mod parse_arg_impl {
use std::fmt;
use super::Amount;
impl parse_arg::ParseArgFromStr for Amount {
fn describe_type<W: fmt::Write>(mut writer: W) -> fmt::Result {
writer.write_str("millisatoshis - a non-negative integer up to 2 100 000 000 000 000 000")
}
}
}
#[cfg(feature = "serde")]
mod serde_impl {
use std::fmt;
use super::Amount;
use serde::{Serialize, Deserialize, Serializer, Deserializer, de::{Visitor, Error}};
struct HRVisitor;
impl<'de> Visitor<'de> for HRVisitor {
type Value = Amount;
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str("a non-negative integer up to 2 100 000 000 000 000 000")
}
fn visit_u64<E>(self, v: u64) -> Result<Self::Value, E> where E: Error {
Amount::from_msat(v).map_err(|_| {
E::invalid_value(serde::de::Unexpected::Unsigned(v), &"a non-negative integer up to 2 100 000 000 000 000 000")
})
}
}
#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
impl Serialize for Amount {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: Serializer {
serializer.serialize_u64(self.0)
}
}
#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
impl<'de> Deserialize<'de> for Amount {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: Deserializer<'de> {
deserializer.deserialize_u64(HRVisitor)
}
}
}
#[cfg(feature = "postgres-types")]
mod postgres_impl {
use super::Amount;
use postgres_types::{ToSql, FromSql, IsNull, Type};
use bytes::BytesMut;
use std::error::Error;
use std::convert::TryInto;
#[cfg_attr(docsrs, doc(cfg(feature = "postgres-types")))]
impl ToSql for Amount {
fn to_sql(&self, ty: &Type, out: &mut BytesMut) -> Result<IsNull, Box<dyn Error + Send + Sync + 'static>> {
(self.to_msat() as i64).to_sql(ty, out)
}
fn accepts(ty: &Type) -> bool {
<i64 as ToSql>::accepts(ty)
}
postgres_types::to_sql_checked!();
}
#[cfg_attr(docsrs, doc(cfg(feature = "postgres-types")))]
impl<'a> FromSql<'a> for Amount {
fn from_sql(ty: &Type, raw: &'a [u8]) -> Result<Self, Box<dyn Error + Send + Sync + 'static>> {
let msats = <i64>::from_sql(ty, raw)?
.try_into()
.map_err(Box::new)?;
Amount::from_msat(msats).map_err(|error| Box::new(error) as _)
}
fn accepts(ty: &Type) -> bool {
<i64 as FromSql>::accepts(ty)
}
}
}
#[cfg(feature = "slog")]
mod slog_impl {
use super::Amount;
use slog::{Key, Value, Record, Serializer};
#[cfg_attr(docsrs, doc(cfg(feature = "slog")))]
impl Value for Amount {
fn serialize(&self, _rec: &Record, key: Key, serializer: &mut dyn Serializer) -> slog::Result {
serializer.emit_u64(key, self.0)
}
}
impl_error_value!(super::ParseError, super::OverflowError, super::FractionError);
}
#[cfg(test)]
mod tests {
use super::Amount;
#[test]
fn amount_max() {
assert_eq!(Amount::from_msat(super::MAX_MONEY_MSAT).unwrap(), Amount::MAX);
}
}