From 42e5043b33a5959aa9ca3115e96347cc768d31f1 Mon Sep 17 00:00:00 2001 From: "Tobin C. Harding" Date: Fri, 1 Nov 2024 18:43:06 +1100 Subject: [PATCH] Add from_int_btc group of functions Add/update the from_int group of functions to provide one that errors and one that is const and panics (errors in const context are not useful because one cannot call `unwrap` in const context). --- bitcoin/examples/taproot-psbt.rs | 6 +++--- units/src/amount/signed.rs | 32 +++++++++++++++++++++++++++++++- units/src/amount/tests.rs | 4 ++-- units/src/amount/unsigned.rs | 26 +++++++++++++++++++++----- 4 files changed, 57 insertions(+), 11 deletions(-) diff --git a/bitcoin/examples/taproot-psbt.rs b/bitcoin/examples/taproot-psbt.rs index 9d418a50c..4a69d718f 100644 --- a/bitcoin/examples/taproot-psbt.rs +++ b/bitcoin/examples/taproot-psbt.rs @@ -49,7 +49,7 @@ const UTXO_1: P2trUtxo = P2trUtxo { script_pubkey: UTXO_SCRIPT_PUBKEY, pubkey: UTXO_PUBKEY, master_fingerprint: UTXO_MASTER_FINGERPRINT, - amount_in_sats: Amount::from_int_btc(50), + amount_in_sats: Amount::from_int_btc_const(50), derivation_path: BIP86_DERIVATION_PATH, }; @@ -60,7 +60,7 @@ const UTXO_2: P2trUtxo = P2trUtxo { script_pubkey: UTXO_SCRIPT_PUBKEY, pubkey: UTXO_PUBKEY, master_fingerprint: UTXO_MASTER_FINGERPRINT, - amount_in_sats: Amount::from_int_btc(50), + amount_in_sats: Amount::from_int_btc_const(50), derivation_path: BIP86_DERIVATION_PATH, }; @@ -71,7 +71,7 @@ const UTXO_3: P2trUtxo = P2trUtxo { script_pubkey: UTXO_SCRIPT_PUBKEY, pubkey: UTXO_PUBKEY, master_fingerprint: UTXO_MASTER_FINGERPRINT, - amount_in_sats: Amount::from_int_btc(50), + amount_in_sats: Amount::from_int_btc_const(50), derivation_path: BIP86_DERIVATION_PATH, }; diff --git a/units/src/amount/signed.rs b/units/src/amount/signed.rs index ed365f6ac..30f43a3f1 100644 --- a/units/src/amount/signed.rs +++ b/units/src/amount/signed.rs @@ -52,12 +52,42 @@ impl SignedAmount { /// Gets the number of satoshis in this [`SignedAmount`]. pub const fn to_sat(self) -> i64 { self.0 } - /// Convert from a value expressing bitcoins to an [`SignedAmount`]. + /// Convert from a value expressing bitcoins to a [`SignedAmount`]. #[cfg(feature = "alloc")] pub fn from_btc(btc: f64) -> Result { SignedAmount::from_float_in(btc, Denomination::Bitcoin) } + /// Converts from a value expressing a whole number of bitcoin to a [`SignedAmount`]. + /// + /// # Errors + /// + /// The function errors if the argument multiplied by the number of sats + /// per bitcoin overflows an `i64` type. + pub fn from_int_btc(btc: i64) -> Result { + match btc.checked_mul(100_000_000) { + Some(amount) => Ok(SignedAmount::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`] + /// in const context. + /// + /// # Panics + /// + /// The function panics if the argument multiplied by the number of sats + /// per bitcoin overflows an `i64` type. + pub const fn from_int_btc_const(btc: i64) -> SignedAmount { + match btc.checked_mul(100_000_000) { + Some(amount) => SignedAmount::from_sat(amount), + None => panic!("checked_mul overflowed"), + } + } + /// Parse a decimal string as a value in the given denomination. /// /// Note: This only parses the value string. If you want to parse a value diff --git a/units/src/amount/tests.rs b/units/src/amount/tests.rs index a695bb101..9c3e6bf9d 100644 --- a/units/src/amount/tests.rs +++ b/units/src/amount/tests.rs @@ -65,13 +65,13 @@ fn from_str_zero_without_denomination() { #[test] fn from_int_btc() { - let amt = Amount::from_int_btc(2); + let amt = Amount::from_int_btc_const(2); assert_eq!(Amount::from_sat(200_000_000), amt); } #[should_panic] #[test] -fn from_int_btc_panic() { Amount::from_int_btc(u64::MAX); } +fn from_int_btc_panic() { Amount::from_int_btc_const(u64::MAX); } #[test] fn test_signed_amount_try_from_amount() { diff --git a/units/src/amount/unsigned.rs b/units/src/amount/unsigned.rs index 46738bbd7..bec99c80d 100644 --- a/units/src/amount/unsigned.rs +++ b/units/src/amount/unsigned.rs @@ -46,9 +46,9 @@ impl Amount { /// Exactly one satoshi. pub const ONE_SAT: Amount = Amount(1); /// Exactly one bitcoin. - pub const ONE_BTC: Amount = Self::from_int_btc(1); + pub const ONE_BTC: Amount = Self::from_int_btc_const(1); /// The maximum value allowed as an amount. Useful for sanity checking. - pub const MAX_MONEY: Amount = Self::from_int_btc(21_000_000); + pub const MAX_MONEY: Amount = Self::from_int_btc_const(21_000_000); /// The minimum value of an amount. pub const MIN: Amount = Amount::ZERO; /// The maximum value of an amount. @@ -68,14 +68,30 @@ impl Amount { Amount::from_float_in(btc, Denomination::Bitcoin) } - /// Converts from a value expressing integer values of bitcoins to an [`Amount`] + /// Converts from a value expressing a whole number of bitcoin to an [`Amount`]. + /// + /// # Errors + /// + /// The function errors if the argument multiplied by the number of sats + /// per bitcoin overflows a `u64` type. + pub fn from_int_btc(btc: u64) -> Result { + match btc.checked_mul(100_000_000) { + Some(amount) => Ok(Amount::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`] /// in const context. /// /// # 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(btc: u64) -> Amount { + /// per bitcoin overflows a `u64` type. + pub const fn from_int_btc_const(btc: u64) -> Amount { match btc.checked_mul(100_000_000) { Some(amount) => Amount::from_sat(amount), None => panic!("checked_mul overflowed"),