units: Make from_int_btc_const take a 16 bit integer

The `from_int_btc_const` constructors are specifically designed for
easily creating amount types in const context but currently they return
an error which is annoying to handle in const context. If we make the
`whole_bitcoin` parameter a 16 bit integer this gives us a nicer const
constructor with the downside that it can only create values upto a
maximum of

- unsigned: 65_536
- signed: 32_767

That is plenty high enough for most use cases.

Then use the new `from_int_btc_const` in associated consts.

Note that because `from_sat` checks max (and min) values we must
define max and min from sats directly.
This commit is contained in:
Tobin C. Harding 2025-03-18 09:37:10 +11:00 committed by Andrew Poelstra
parent cfccd389a9
commit 2ec1c2a044
No known key found for this signature in database
GPG Key ID: C588D63CE41B97C1
3 changed files with 19 additions and 31 deletions

View File

@ -77,9 +77,9 @@ impl SignedAmount {
/// Exactly one satoshi.
pub const ONE_SAT: Self = SignedAmount::from_sat_unchecked(1);
/// Exactly one bitcoin.
pub const ONE_BTC: Self = SignedAmount::from_sat_unchecked(100_000_000);
pub const ONE_BTC: Self = SignedAmount::from_int_btc_const(1);
/// Exactly fifty bitcoin.
pub const FIFTY_BTC: Self = SignedAmount::from_sat_unchecked(50 * 100_000_000);
pub const FIFTY_BTC: Self = SignedAmount::from_int_btc_const(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.
@ -134,27 +134,21 @@ impl SignedAmount {
}
/// Converts from a value expressing a whole number of bitcoin to a [`SignedAmount`].
///
/// # Errors
///
/// If `whole_bitcoin` is greater than `21_000_000`.
#[allow(clippy::missing_panics_doc)]
pub fn from_int_btc<T: Into<i32>>(whole_bitcoin: T) -> Result<SignedAmount, OutOfRangeError> {
pub fn from_int_btc<T: Into<i16>>(whole_bitcoin: T) -> SignedAmount {
SignedAmount::from_int_btc_const(whole_bitcoin.into())
}
/// Converts from a value expressing a whole number of bitcoin to a [`SignedAmount`]
/// in const context.
///
/// # Errors
///
/// If `whole_bitcoin` is greater than `21_000_000`.
#[allow(clippy::missing_panics_doc)]
pub const fn from_int_btc_const(whole_bitcoin: i32) -> Result<SignedAmount, OutOfRangeError> {
pub const fn from_int_btc_const(whole_bitcoin: i16) -> SignedAmount {
let btc = whole_bitcoin as i64; // Can't call `into` in const context.
match btc.checked_mul(100_000_000) {
Some(amount) => SignedAmount::from_sat(amount),
None => panic!("cannot overflow in i64"),
let sats = btc * 100_000_000;
match SignedAmount::from_sat(sats) {
Ok(amount) => amount,
Err(_) => panic!("unreachable - 65536 BTC is within range"),
}
}

View File

@ -102,7 +102,7 @@ fn from_str_zero_without_denomination() {
#[test]
fn from_int_btc() {
let amt = Amount::from_int_btc_const(2).unwrap();
let amt = Amount::from_int_btc_const(2);
assert_eq!(sat(200_000_000), amt);
}

View File

@ -75,9 +75,9 @@ impl Amount {
/// Exactly one satoshi.
pub const ONE_SAT: Self = Amount::from_sat_unchecked(1);
/// Exactly one bitcoin.
pub const ONE_BTC: Self = Amount::from_sat_unchecked(100_000_000);
pub const ONE_BTC: Self = Amount::from_int_btc_const(1);
/// Exactly fifty bitcoin.
pub const FIFTY_BTC: Self = Amount::from_sat_unchecked(50 * 100_000_000);
pub const FIFTY_BTC: Self = Amount::from_int_btc_const(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.
@ -132,27 +132,21 @@ impl Amount {
}
/// Converts from a value expressing a whole number of bitcoin to an [`Amount`].
///
/// # Errors
///
/// If `whole_bitcoin` is greater than `21_000_000`.
#[allow(clippy::missing_panics_doc)]
pub fn from_int_btc<T: Into<u32>>(whole_bitcoin: T) -> Result<Amount, OutOfRangeError> {
pub fn from_int_btc<T: Into<u16>>(whole_bitcoin: T) -> Amount {
Amount::from_int_btc_const(whole_bitcoin.into())
}
/// Converts from a value expressing a whole number of bitcoin to an [`Amount`]
/// in const context.
///
/// # Errors
///
/// If `whole_bitcoin` is greater than `21_000_000`.
#[allow(clippy::missing_panics_doc)]
pub const fn from_int_btc_const(whole_bitcoin: u32) -> Result<Amount, OutOfRangeError> {
pub const fn from_int_btc_const(whole_bitcoin: u16) -> Amount {
let btc = whole_bitcoin as u64; // Can't call `into` in const context.
match btc.checked_mul(100_000_000) {
Some(amount) => Amount::from_sat(amount),
None => panic!("cannot overflow a u64"),
let sats = btc * 100_000_000;
match Amount::from_sat(sats) {
Ok(amount) => amount,
Err(_) => panic!("unreachable - 65536 BTC is within range"),
}
}