Fix amount whole bitcoin constructors

I royally botched the recent effort to make const amount constructors
use a smaller type. I left in an  unnecessary panic and forgot to do
both of them.

Note these function return values will change again very shortly when we
start enforcing the MAX_MONEY invariant. However the 64 to 32 bit change
is unrelated to that and is easier to review if done separately.

Whole bitcoin can not in any sane environment be greater than 21,000,000
which fits in 32 bits so we can take a 32 bit integer in the whole
bitcoin constructors without loss of utility. Doing so removes the
potential panic.

This is a breaking API change. We elect not to deprecate because we want
to keep the same function names.
This commit is contained in:
Tobin C. Harding 2025-02-28 10:45:34 +11:00
parent ac71680202
commit 13595fbe7d
No known key found for this signature in database
GPG Key ID: 40BF9E4C269D6607
2 changed files with 14 additions and 35 deletions

View File

@ -121,29 +121,19 @@ impl SignedAmount {
} }
/// Converts from a value expressing a whole number of bitcoin to a [`SignedAmount`]. /// Converts from a value expressing a whole number of bitcoin to a [`SignedAmount`].
/// #[allow(clippy::missing_panics_doc)]
/// # Errors pub fn from_int_btc<T: Into<i32>>(whole_bitcoin: T) -> SignedAmount {
/// SignedAmount::from_int_btc_const(whole_bitcoin.into())
/// The function errors if the argument multiplied by the number of sats
/// per bitcoin overflows an `i64` type.
pub fn from_int_btc<T: Into<i64>>(whole_bitcoin: T) -> Result<SignedAmount, OutOfRangeError> {
match whole_bitcoin.into().checked_mul(100_000_000) {
Some(amount) => Ok(Self::from_sat(amount)),
None => Err(OutOfRangeError { is_signed: true, is_greater_than_max: true }),
}
} }
/// Converts from a value expressing a whole number of bitcoin to a [`SignedAmount`] /// Converts from a value expressing a whole number of bitcoin to a [`SignedAmount`]
/// in const context. /// in const context.
/// #[allow(clippy::missing_panics_doc)]
/// # Panics pub const fn from_int_btc_const(whole_bitcoin: i32) -> SignedAmount {
/// let btc = whole_bitcoin as i64; // Can't call `into` in const context.
/// The function panics if the argument multiplied by the number of sats match btc.checked_mul(100_000_000) {
/// per bitcoin overflows an `i64` type.
pub const fn from_int_btc_const(whole_bitcoin: i64) -> SignedAmount {
match whole_bitcoin.checked_mul(100_000_000) {
Some(amount) => SignedAmount::from_sat(amount), Some(amount) => SignedAmount::from_sat(amount),
None => panic!("checked_mul overflowed"), None => panic!("cannot overflow in i64"),
} }
} }

View File

@ -119,30 +119,19 @@ impl Amount {
} }
/// Converts from a value expressing a whole number of bitcoin to an [`Amount`]. /// Converts from a value expressing a whole number of bitcoin to an [`Amount`].
/// #[allow(clippy::missing_panics_doc)]
/// # Errors pub fn from_int_btc<T: Into<u32>>(whole_bitcoin: T) -> Amount {
/// Amount::from_int_btc_const(whole_bitcoin.into())
/// The function errors if the argument multiplied by the number of sats
/// per bitcoin overflows a `u64` type.
pub fn from_int_btc<T: Into<u64>>(whole_bitcoin: T) -> Result<Amount, OutOfRangeError> {
match whole_bitcoin.into().checked_mul(100_000_000) {
Some(amount) => Ok(Self::from_sat(amount)),
None => Err(OutOfRangeError { is_signed: false, is_greater_than_max: true }),
}
} }
/// Converts from a value expressing a whole number of bitcoin to an [`Amount`] /// Converts from a value expressing a whole number of bitcoin to an [`Amount`]
/// in const context. /// in const context.
/// #[allow(clippy::missing_panics_doc)]
/// # Panics
///
/// The function panics if the argument multiplied by the number of sats
/// per bitcoin overflows a `u64` type.
pub const fn from_int_btc_const(whole_bitcoin: u32) -> Amount { pub const fn from_int_btc_const(whole_bitcoin: u32) -> Amount {
let btc = whole_bitcoin as u64; // Can't call u64::from in const context. let btc = whole_bitcoin as u64; // Can't call `into` in const context.
match btc.checked_mul(100_000_000) { match btc.checked_mul(100_000_000) {
Some(amount) => Amount::from_sat(amount), Some(amount) => Amount::from_sat(amount),
None => panic!("checked_mul overflowed"), None => panic!("cannot overflow a u64"),
} }
} }