amount: move MIN/MAX constants and constructors inside the privacy boundary

It's conceptually a bit tortured to have an `Amount` type defined in a
private module, with an _unchecked method allowing you to set values out
of range, which needs to be used outside of the module to *define* the
range and the constructors that check it.

Move the constants and constructors inside the privacy module, where they
can be written directly. This is easier to understand and eliminates a couple
_unchecked calls.
This commit is contained in:
Andrew Poelstra 2025-03-18 14:28:18 +00:00
parent 004d073184
commit d0d7a15604
No known key found for this signature in database
GPG Key ID: C588D63CE41B97C1
2 changed files with 64 additions and 58 deletions

View File

@ -17,6 +17,8 @@ use super::{
};
mod encapsulate {
use super::OutOfRangeError;
/// A signed amount.
///
/// The [`SignedAmount`] type can be used to express Bitcoin amounts that support arithmetic and
@ -50,6 +52,11 @@ mod encapsulate {
pub struct SignedAmount(i64);
impl SignedAmount {
/// The maximum value of an amount.
pub const MAX: Self = Self(21_000_000 * 100_000_000);
/// The minimum value of an amount.
pub const MIN: Self = Self(-21_000_000 * 100_000_000);
/// Constructs a new [`SignedAmount`] with satoshi precision and the given number of satoshis.
///
/// Caller to guarantee that `satoshi` is within valid range.
@ -74,26 +81,6 @@ mod encapsulate {
/// assert_eq!(SignedAmount::ONE_BTC.to_sat(), 100_000_000);
/// ```
pub const fn to_sat(self) -> i64 { self.0 }
}
}
#[doc(inline)]
pub use encapsulate::SignedAmount;
impl SignedAmount {
/// The zero amount.
pub const ZERO: Self = SignedAmount::from_sat_i32(0);
/// Exactly one satoshi.
pub const ONE_SAT: Self = SignedAmount::from_sat_i32(1);
/// Exactly one bitcoin.
pub const ONE_BTC: Self = SignedAmount::from_btc_i16(1);
/// Exactly fifty bitcoin.
pub const FIFTY_BTC: Self = SignedAmount::from_btc_i16(50);
/// The maximum value allowed as an amount. Useful for sanity checking.
pub const MAX_MONEY: Self = SignedAmount::from_sat_unchecked(21_000_000 * 100_000_000);
/// The minimum value of an amount.
pub const MIN: Self = SignedAmount::from_sat_unchecked(-21_000_000 * 100_000_000);
/// The maximum value of an amount.
pub const MAX: Self = SignedAmount::MAX_MONEY;
/// Constructs a new [`SignedAmount`] from the given number of satoshis.
///
@ -116,9 +103,25 @@ impl SignedAmount {
} else if satoshi > Self::MAX_MONEY.to_sat() {
Err(OutOfRangeError { is_signed: true, is_greater_than_max: true })
} else {
Ok(SignedAmount::from_sat_unchecked(satoshi))
Ok(Self(satoshi))
}
}
}
}
#[doc(inline)]
pub use encapsulate::SignedAmount;
impl SignedAmount {
/// The zero amount.
pub const ZERO: Self = SignedAmount::from_sat_i32(0);
/// Exactly one satoshi.
pub const ONE_SAT: Self = SignedAmount::from_sat_i32(1);
/// Exactly one bitcoin.
pub const ONE_BTC: Self = SignedAmount::from_btc_i16(1);
/// Exactly fifty bitcoin.
pub const FIFTY_BTC: Self = SignedAmount::from_btc_i16(50);
/// The maximum value allowed as an amount. Useful for sanity checking.
pub const MAX_MONEY: Self = Self::MAX;
/// Converts from a value expressing a decimal number of bitcoin to a [`SignedAmount`].
///

View File

@ -17,6 +17,8 @@ use super::{
};
mod encapsulate {
use super::OutOfRangeError;
/// An amount.
///
/// The [`Amount`] type can be used to express Bitcoin amounts that support arithmetic and
@ -50,6 +52,11 @@ mod encapsulate {
pub struct Amount(u64);
impl Amount {
/// The maximum value of an amount.
pub const MAX: Self = Self(21_000_000 * 100_000_000);
/// The minimum value of an amount.
pub const MIN: Self = Self(0);
/// Constructs a new [`Amount`] with satoshi precision and the given number of satoshis.
///
/// Caller to guarantee that `satoshi` is within valid range. See [`Self::MAX`].
@ -72,28 +79,6 @@ mod encapsulate {
/// assert_eq!(Amount::ONE_BTC.to_sat(), 100_000_000);
/// ```
pub const fn to_sat(self) -> u64 { self.0 }
}
}
#[doc(inline)]
pub use encapsulate::Amount;
impl Amount {
/// The zero amount.
pub const ZERO: Self = Amount::from_sat_u32(0);
/// Exactly one satoshi.
pub const ONE_SAT: Self = Amount::from_sat_u32(1);
/// Exactly one bitcoin.
pub const ONE_BTC: Self = Amount::from_btc_u16(1);
/// Exactly fifty bitcoin.
pub const FIFTY_BTC: Self = Amount::from_btc_u16(50);
/// The maximum value allowed as an amount. Useful for sanity checking.
pub const MAX_MONEY: Self = Amount::from_sat_unchecked(21_000_000 * 100_000_000);
/// The minimum value of an amount.
pub const MIN: Self = Amount::ZERO;
/// The maximum value of an amount.
pub const MAX: Self = Amount::MAX_MONEY;
/// The number of bytes that an amount contributes to the size of a transaction.
pub const SIZE: usize = 8; // Serialized length of a u64.
/// Constructs a new [`Amount`] from the given number of satoshis.
///
@ -114,9 +99,27 @@ impl Amount {
if satoshi > Self::MAX_MONEY.to_sat() {
Err(OutOfRangeError { is_signed: false, is_greater_than_max: true })
} else {
Ok(Self::from_sat_unchecked(satoshi))
Ok(Self(satoshi))
}
}
}
}
#[doc(inline)]
pub use encapsulate::Amount;
impl Amount {
/// The zero amount.
pub const ZERO: Self = Amount::from_sat_u32(0);
/// Exactly one satoshi.
pub const ONE_SAT: Self = Amount::from_sat_u32(1);
/// Exactly one bitcoin.
pub const ONE_BTC: Self = Amount::from_btc_u16(1);
/// Exactly fifty bitcoin.
pub const FIFTY_BTC: Self = Amount::from_btc_u16(50);
/// The maximum value allowed as an amount. Useful for sanity checking.
pub const MAX_MONEY: Self = Amount::MAX;
/// The number of bytes that an amount contributes to the size of a transaction.
pub const SIZE: usize = 8; // Serialized length of a u64.
/// Converts from a value expressing a decimal number of bitcoin to an [`Amount`].
///