Merge rust-bitcoin/rust-bitcoin#3953: Refactor amounts

13a3f490b8 Use Self instead of amount type (Tobin C. Harding)
34e3049ae0 Use sats instead of satoshi (Tobin C. Harding)
00b71a670f Use from_sat_unchecked for hardcoded ints (Tobin C. Harding)
8fdec67f7d Change local var ua to sat (Tobin C. Harding)
c6f056672b Change local var sa to ssat (Tobin C. Harding)
f3e853e07a units: Do trivial refactor of amount::tests (Tobin C. Harding)
dbec9807f9 Shorten identifiers by removing _in_sats (Tobin C. Harding)
154a4420fc Stop using FQP on Amount type (Tobin C. Harding)
8e16a48252 Run the formatter (Tobin C. Harding)

Pull request description:

  Do a bunch of refactorings to tease out changes from #3794.

  The first 8 are uncontroversial. The 9th one is subjective. The last one is unusual but IMO worth doing because of the relationship between the two amount modules.

  Do note that this PR is 100% internal changes - please please don't bike shed this to death.

ACKs for top commit:
  apoelstra:
    ACK 13a3f490b80e4c8f8e1753111a914315eefd73e6; successfully ran local tests; lgtm

Tree-SHA512: e2ef0e7fbdaaf632a9840920a227a901fbeb55a29398013cd6cb764b1ff7c0a7c5a1648fd8f606e8b5f7523943886f5eff54cf4054d24349feb72f0b4de05b91
This commit is contained in:
merge-script 2025-01-26 01:57:30 +00:00
commit 72e2b00721
No known key found for this signature in database
GPG Key ID: C588D63CE41B97C1
10 changed files with 82 additions and 107 deletions

View File

@ -148,7 +148,7 @@ fn sighash_p2wpkh() {
let inp_idx = 0;
//output value from the referenced vout:0 from the referenced tx:
//bitcoin-cli getrawtransaction 752d675b9cc0bd14e0bd23969effee0005ad6d7e550dcc832f0216c7ffd4e15c 3
let ref_out_value = Amount::from_sat(200000000);
let ref_out_value = Amount::from_sat_unchecked(200000000);
println!("\nsighash_p2wpkh:");
compute_sighash_p2wpkh(&raw_tx, inp_idx, ref_out_value);
@ -178,7 +178,7 @@ fn sighash_p2wsh_multisig_2x2() {
//For the witness transaction sighash computation, we need its referenced output's value from the original transaction:
//bitcoin-cli getrawtransaction 2845399a8cd7a52733f9f9d0e0b8b6c5d1c88aea4cee09f8d8fa762912b49e1b 3
//we need vout 0 value in sats:
let ref_out_value = Amount::from_sat(968240);
let ref_out_value = Amount::from_sat_unchecked(968240);
println!("\nsighash_p2wsh_multisig_2x2:");
compute_sighash_p2wsh(&raw_tx, 0, ref_out_value);

View File

@ -40,7 +40,7 @@ const UTXO_SCRIPT_PUBKEY: &str =
"5120be27fa8b1f5278faf82cab8da23e8761f8f9bd5d5ebebbb37e0e12a70d92dd16";
const UTXO_PUBKEY: &str = "a6ac32163539c16b6b5dbbca01b725b8e8acaa5f821ba42c80e7940062140d19";
const UTXO_MASTER_FINGERPRINT: &str = "e61b318f";
const ABSOLUTE_FEES_IN_SATS: Amount = Amount::from_sat_unchecked(1_000);
const ABSOLUTE_FEES: Amount = Amount::from_sat_unchecked(1_000);
// UTXO_1 will be used for spending example 1
const UTXO_1: P2trUtxo = P2trUtxo {
@ -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::FIFTY_BTC,
amount: Amount::FIFTY_BTC,
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::FIFTY_BTC,
amount: Amount::FIFTY_BTC,
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::FIFTY_BTC,
amount: Amount::FIFTY_BTC,
derivation_path: BIP86_DERIVATION_PATH,
};
@ -106,11 +106,11 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
let change_address = "bcrt1pz449kexzydh2kaypatup5ultru3ej284t6eguhnkn6wkhswt0l7q3a7j76"
.parse::<Address<_>>()?
.require_network(Network::Regtest)?;
let amount_to_send_in_sats = Amount::ONE_BTC;
let amount_to_send = Amount::ONE_BTC;
let change_amount = UTXO_1
.amount_in_sats
.checked_sub(amount_to_send_in_sats)
.and_then(|x| x.checked_sub(ABSOLUTE_FEES_IN_SATS))
.amount
.checked_sub(amount_to_send)
.and_then(|x| x.checked_sub(ABSOLUTE_FEES))
.ok_or("fees more than input amount!")?;
let tx_hex_string = encode::serialize_hex(&generate_bip86_key_spend_tx(
@ -120,7 +120,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
// Set these fields with valid data for the UTXO from step 5 above
UTXO_1,
vec![
TxOut { value: amount_to_send_in_sats, script_pubkey: to_address.script_pubkey() },
TxOut { value: amount_to_send, script_pubkey: to_address.script_pubkey() },
TxOut { value: change_amount, script_pubkey: change_address.script_pubkey() },
],
)?);
@ -215,7 +215,7 @@ struct P2trUtxo<'a> {
script_pubkey: &'a str,
pubkey: &'a str,
master_fingerprint: &'a str,
amount_in_sats: Amount,
amount: Amount,
derivation_path: &'a str,
}
@ -226,7 +226,7 @@ fn generate_bip86_key_spend_tx(
input_utxo: P2trUtxo,
outputs: Vec<TxOut>,
) -> Result<Transaction, Box<dyn std::error::Error>> {
let from_amount = input_utxo.amount_in_sats;
let from_amount = input_utxo.amount;
let input_pubkey = input_utxo.pubkey.parse::<XOnlyPublicKey>()?;
// CREATOR + UPDATER
@ -274,7 +274,7 @@ fn generate_bip86_key_spend_tx(
let mut input_txouts = Vec::<TxOut>::new();
for input in [&input_utxo].iter() {
input_txouts.push(TxOut {
value: input.amount_in_sats,
value: input.amount,
script_pubkey: ScriptBuf::from_hex(input.script_pubkey)?,
});
}
@ -412,7 +412,7 @@ impl BenefactorWallet {
taproot_spend_info.internal_key(),
taproot_spend_info.merkle_root(),
);
let value = input_utxo.amount_in_sats - ABSOLUTE_FEES_IN_SATS;
let value = input_utxo.amount - ABSOLUTE_FEES;
// Spend a normal BIP86-like output as an input in our inheritance funding transaction
let tx = generate_bip86_key_spend_tx(
@ -476,7 +476,7 @@ impl BenefactorWallet {
let mut psbt = self.next_psbt.clone().expect("should have next_psbt");
let input = &mut psbt.inputs[0];
let input_value = input.witness_utxo.as_ref().unwrap().value;
let output_value = input_value - ABSOLUTE_FEES_IN_SATS;
let output_value = input_value - ABSOLUTE_FEES;
// We use some other derivation path in this example for our inheritance protocol. The important thing is to ensure
// that we use an unhardened path so we can make use of xpubs.
@ -649,7 +649,7 @@ impl BeneficiaryWallet {
psbt.unsigned_tx.lock_time = lock_time;
psbt.unsigned_tx.output = vec![TxOut {
script_pubkey: to_address.script_pubkey(),
value: input_value - ABSOLUTE_FEES_IN_SATS,
value: input_value - ABSOLUTE_FEES,
}];
psbt.outputs = vec![Output::default()];
let unsigned_tx = psbt.unsigned_tx.clone();

View File

@ -112,10 +112,7 @@ fn bitcoin_genesis_tx(params: &Params) -> Transaction {
witness: Witness::default(),
});
ret.output.push(TxOut {
value: Amount::FIFTY_BTC,
script_pubkey: out_script,
});
ret.output.push(TxOut { value: Amount::FIFTY_BTC, script_pubkey: out_script });
// end
ret

View File

@ -15,7 +15,7 @@ use crate::opcodes::{self, Opcode};
use crate::policy::{DUST_RELAY_TX_FEE, MAX_OP_RETURN_RELAY};
use crate::prelude::{sink, DisplayHex, String, ToString};
use crate::taproot::{LeafVersion, TapLeafHash, TapLeafHashExt as _};
use crate::FeeRate;
use crate::{Amount, FeeRate};
#[rustfmt::skip] // Keep public re-exports separate.
#[doc(inline)]
@ -261,7 +261,7 @@ crate::internal_macros::define_extension_trait! {
/// Returns the minimum value an output with this script should have in order to be
/// broadcastable on todays Bitcoin network.
#[deprecated(since = "0.32.0", note = "use `minimal_non_dust` etc. instead")]
fn dust_value(&self) -> crate::Amount { self.minimal_non_dust() }
fn dust_value(&self) -> Amount { self.minimal_non_dust() }
/// Returns the minimum value an output with this script should have in order to be
/// broadcastable on today's Bitcoin network.
@ -272,7 +272,7 @@ crate::internal_macros::define_extension_trait! {
/// To use a custom value, use [`minimal_non_dust_custom`].
///
/// [`minimal_non_dust_custom`]: Script::minimal_non_dust_custom
fn minimal_non_dust(&self) -> crate::Amount {
fn minimal_non_dust(&self) -> Amount {
self.minimal_non_dust_internal(DUST_RELAY_TX_FEE.into())
}
@ -287,7 +287,7 @@ crate::internal_macros::define_extension_trait! {
/// To use the default Bitcoin Core value, use [`minimal_non_dust`].
///
/// [`minimal_non_dust`]: Script::minimal_non_dust
fn minimal_non_dust_custom(&self, dust_relay_fee: FeeRate) -> crate::Amount {
fn minimal_non_dust_custom(&self, dust_relay_fee: FeeRate) -> Amount {
self.minimal_non_dust_internal(dust_relay_fee.to_sat_per_kwu() * 4)
}
@ -394,7 +394,7 @@ mod sealed {
crate::internal_macros::define_extension_trait! {
pub(crate) trait ScriptExtPriv impl for Script {
fn minimal_non_dust_internal(&self, dust_relay_fee: u64) -> crate::Amount {
fn minimal_non_dust_internal(&self, dust_relay_fee: u64) -> Amount {
// This must never be lower than Bitcoin Core's GetDustThreshold() (as of v0.21) as it may
// otherwise allow users to create transactions which likely can never be broadcast/confirmed.
let sats = dust_relay_fee
@ -414,7 +414,7 @@ crate::internal_macros::define_extension_trait! {
// Note: We ensure the division happens at the end, since Core performs the division at the end.
// This will make sure none of the implicit floor operations mess with the value.
crate::Amount::from_sat(sats)
Amount::from_sat(sats)
}
fn count_sigops_internal(&self, accurate: bool) -> usize {

View File

@ -9,7 +9,7 @@ use crate::address::script_pubkey::{
};
use crate::consensus::encode::{deserialize, serialize};
use crate::crypto::key::{PublicKey, XOnlyPublicKey};
use crate::FeeRate;
use crate::{Amount, FeeRate};
#[test]
#[rustfmt::skip]
@ -676,7 +676,7 @@ fn bitcoinconsensus() {
let spent_bytes = hex!("0020701a8d401c84fb13e6baf169d59684e17abd9fa216c8cc5b9fc63d622ff8c58d");
let spent = Script::from_bytes(&spent_bytes);
let spending = hex!("010000000001011f97548fbbe7a0db7588a66e18d803d0089315aa7d4cc28360b6ec50ef36718a0100000000ffffffff02df1776000000000017a9146c002a686959067f4866b8fb493ad7970290ab728757d29f0000000000220020701a8d401c84fb13e6baf169d59684e17abd9fa216c8cc5b9fc63d622ff8c58d04004730440220565d170eed95ff95027a69b313758450ba84a01224e1f7f130dda46e94d13f8602207bdd20e307f062594022f12ed5017bbf4a055a06aea91c10110a0e3bb23117fc014730440220647d2dc5b15f60bc37dc42618a370b2a1490293f9e5c8464f53ec4fe1dfe067302203598773895b4b16d37485cbe21b337f4e4b650739880098c592553add7dd4355016952210375e00eb72e29da82b89367947f29ef34afb75e8654f6ea368e0acdfd92976b7c2103a1b26313f430c4b15bb1fdce663207659d8cac749a0e53d70eff01874496feff2103c96d495bfdd5ba4145e3e046fee45e84a8a48ad05bd8dbb395c011a32cf9f88053ae00000000");
spent.verify(0, crate::Amount::from_sat_unchecked(18393430), &spending).unwrap();
spent.verify(0, Amount::from_sat_unchecked(18393430), &spending).unwrap();
}
#[test]
@ -685,10 +685,10 @@ fn default_dust_value() {
// well-known scriptPubKey types.
let script_p2wpkh = Builder::new().push_int_unchecked(0).push_slice([42; 20]).into_script();
assert!(script_p2wpkh.is_p2wpkh());
assert_eq!(script_p2wpkh.minimal_non_dust(), crate::Amount::from_sat_unchecked(294));
assert_eq!(script_p2wpkh.minimal_non_dust(), Amount::from_sat_unchecked(294));
assert_eq!(
script_p2wpkh.minimal_non_dust_custom(FeeRate::from_sat_per_vb_unchecked(6)),
crate::Amount::from_sat_unchecked(588)
Amount::from_sat_unchecked(588)
);
let script_p2pkh = Builder::new()
@ -699,10 +699,10 @@ fn default_dust_value() {
.push_opcode(OP_CHECKSIG)
.into_script();
assert!(script_p2pkh.is_p2pkh());
assert_eq!(script_p2pkh.minimal_non_dust(), crate::Amount::from_sat_unchecked(546));
assert_eq!(script_p2pkh.minimal_non_dust(), Amount::from_sat_unchecked(546));
assert_eq!(
script_p2pkh.minimal_non_dust_custom(FeeRate::from_sat_per_vb_unchecked(6)),
crate::Amount::from_sat_unchecked(1092)
Amount::from_sat_unchecked(1092)
);
}

View File

@ -134,7 +134,7 @@ define_extension_trait! {
fn verify(
&self,
index: usize,
amount: crate::Amount,
amount: Amount,
spending_tx: &[u8],
) -> Result<(), BitcoinconsensusError> {
verify_script(self, index, amount, spending_tx)
@ -153,7 +153,7 @@ define_extension_trait! {
fn verify_with_flags(
&self,
index: usize,
amount: crate::Amount,
amount: Amount,
spending_tx: &[u8],
flags: impl Into<u32>,
) -> Result<(), BitcoinconsensusError> {

View File

@ -109,7 +109,7 @@ impl SignedAmount {
/// ```
#[cfg(feature = "alloc")]
pub fn from_btc(btc: f64) -> Result<SignedAmount, ParseAmountError> {
SignedAmount::from_float_in(btc, Denomination::Bitcoin)
Self::from_float_in(btc, Denomination::Bitcoin)
}
/// Converts from a value expressing a whole number of bitcoin to a [`SignedAmount`].
@ -120,7 +120,7 @@ impl SignedAmount {
/// 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(SignedAmount::from_sat(amount)),
Some(amount) => Ok(Self::from_sat(amount)),
None => Err(OutOfRangeError { is_signed: true, is_greater_than_max: true }),
}
}
@ -180,7 +180,7 @@ impl SignedAmount {
/// ```
pub fn from_str_with_denomination(s: &str) -> Result<SignedAmount, ParseError> {
let (amt, denom) = split_amount_and_denomination(s)?;
SignedAmount::from_str_in(amt, denom).map_err(Into::into)
Self::from_str_in(amt, denom).map_err(Into::into)
}
/// Expresses this [`SignedAmount`] as a floating-point value in the given [`Denomination`].
@ -228,7 +228,7 @@ impl SignedAmount {
) -> Result<SignedAmount, ParseAmountError> {
// This is inefficient, but the safest way to deal with this. The parsing logic is safe.
// Any performance-critical application should not be dealing with floats.
SignedAmount::from_str_in(&value.to_string(), denom)
Self::from_str_in(&value.to_string(), denom)
}
/// Constructs a new object that implements [`fmt::Display`] in the given [`Denomination`].
@ -548,14 +548,14 @@ impl FromStr for SignedAmount {
///
/// If the returned value would be zero or negative zero, then no denomination is required.
fn from_str(s: &str) -> Result<Self, Self::Err> {
let result = SignedAmount::from_str_with_denomination(s);
let result = Self::from_str_with_denomination(s);
match result {
Err(ParseError(ParseErrorInner::MissingDenomination(_))) => {
let d = SignedAmount::from_str_in(s, Denomination::Satoshi);
let d = Self::from_str_in(s, Denomination::Satoshi);
if d == Ok(SignedAmount::ZERO) {
Ok(SignedAmount::ZERO)
if d == Ok(Self::ZERO) {
Ok(Self::ZERO)
} else {
result
}
@ -568,14 +568,14 @@ impl FromStr for SignedAmount {
impl From<Amount> for SignedAmount {
fn from(value: Amount) -> Self {
let v = value.to_sat() as i64; // Cast ok, signed amount and amount share positive range.
SignedAmount::from_sat_unchecked(v)
Self::from_sat_unchecked(v)
}
}
impl core::iter::Sum for SignedAmount {
fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
let sats: i64 = iter.map(|amt| amt.0).sum();
SignedAmount::from_sat(sats)
Self::from_sat(sats)
}
}
@ -585,7 +585,7 @@ impl<'a> core::iter::Sum<&'a SignedAmount> for SignedAmount {
I: Iterator<Item = &'a SignedAmount>,
{
let sats: i64 = iter.map(|amt| amt.0).sum();
SignedAmount::from_sat(sats)
Self::from_sat(sats)
}
}
@ -593,6 +593,6 @@ impl<'a> core::iter::Sum<&'a SignedAmount> for SignedAmount {
impl<'a> Arbitrary<'a> for SignedAmount {
fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
let s = i64::arbitrary(u)?;
Ok(SignedAmount(s))
Ok(Self(s))
}
}

View File

@ -22,10 +22,7 @@ fn sanity_check() {
let ssat = SignedAmount::from_sat;
assert_eq!(ssat(-100).abs(), ssat(100));
assert_eq!(
ssat(i64::MIN + 1).checked_abs().unwrap(),
ssat(i64::MAX)
);
assert_eq!(ssat(i64::MIN + 1).checked_abs().unwrap(), ssat(i64::MAX));
assert_eq!(ssat(-100).signum(), -1);
assert_eq!(ssat(0).signum(), 0);
assert_eq!(ssat(100).signum(), 1);
@ -36,14 +33,8 @@ fn sanity_check() {
#[cfg(feature = "alloc")]
{
assert_eq!(
Amount::from_float_in(0_f64, Denomination::Bitcoin).unwrap(),
sat(0)
);
assert_eq!(
Amount::from_float_in(2_f64, Denomination::Bitcoin).unwrap(),
sat(200_000_000)
);
assert_eq!(Amount::from_float_in(0_f64, Denomination::Bitcoin).unwrap(), sat(0));
assert_eq!(Amount::from_float_in(2_f64, Denomination::Bitcoin).unwrap(), sat(200_000_000));
assert!(Amount::from_float_in(-100_f64, Denomination::Bitcoin).is_err());
}
}
@ -151,7 +142,7 @@ fn mul_div() {
#[test]
fn neg() {
let amount = -SignedAmount::from_sat(2);
let amount = -SignedAmount::from_sat_unchecked(2);
assert_eq!(amount.to_sat(), -2);
}
@ -185,14 +176,8 @@ fn unchecked_arithmetic() {
let sat = Amount::from_sat;
let ssat = SignedAmount::from_sat;
assert_eq!(
ssat(10).unchecked_add(ssat(20)),
ssat(30)
);
assert_eq!(
ssat(50).unchecked_sub(ssat(10)),
ssat(40)
);
assert_eq!(ssat(10).unchecked_add(ssat(20)), ssat(30));
assert_eq!(ssat(50).unchecked_sub(ssat(10)), ssat(40));
assert_eq!(sat(5).unchecked_add(sat(7)), sat(12));
assert_eq!(sat(10).unchecked_sub(sat(7)), sat(3));
}
@ -201,10 +186,7 @@ fn unchecked_arithmetic() {
fn positive_sub() {
let ssat = SignedAmount::from_sat;
assert_eq!(
ssat(10).positive_sub(ssat(7)).unwrap(),
ssat(3)
);
assert_eq!(ssat(10).positive_sub(ssat(7)).unwrap(), ssat(3));
assert!(ssat(-10).positive_sub(ssat(7)).is_none());
assert!(ssat(10).positive_sub(ssat(-7)).is_none());
assert!(ssat(10).positive_sub(ssat(11)).is_none());
@ -249,7 +231,7 @@ fn amount_checked_div_by_weight_floor() {
#[cfg(feature = "alloc")]
#[test]
fn amount_checked_div_by_fee_rate() {
let amount = Amount::from_sat(1000);
let amount = Amount::from_sat_unchecked(1000);
let fee_rate = FeeRate::from_sat_per_kwu(2);
// Test floor division
@ -262,7 +244,7 @@ fn amount_checked_div_by_fee_rate() {
assert_eq!(weight, Weight::from_wu(500_000)); // Same result for exact division
// Test truncation behavior
let amount = Amount::from_sat(1000);
let amount = Amount::from_sat_unchecked(1000);
let fee_rate = FeeRate::from_sat_per_kwu(3);
let floor_weight = amount.checked_div_by_fee_rate_floor(fee_rate).unwrap();
let ceil_weight = amount.checked_div_by_fee_rate_ceil(fee_rate).unwrap();
@ -589,15 +571,13 @@ check_format_non_negative_show_denom! {
#[test]
fn unsigned_signed_conversion() {
let sa = SignedAmount::from_sat;
let ua = Amount::from_sat;
let ssat = SignedAmount::from_sat;
let sat = Amount::from_sat;
let max_sats: u64 = Amount::MAX.to_sat();
assert_eq!(ua(max_sats).to_signed(), sa(max_sats as i64));
assert_eq!(sa(max_sats as i64).to_unsigned(), Ok(ua(max_sats)));
assert_eq!(sa(max_sats as i64).to_unsigned().unwrap().to_signed(), sa(max_sats as i64));
assert_eq!(sat(max_sats).to_signed(), ssat(max_sats as i64));
assert_eq!(ssat(max_sats as i64).to_unsigned(), Ok(sat(max_sats)));
assert_eq!(ssat(max_sats as i64).to_unsigned().unwrap().to_signed(), ssat(max_sats as i64));
}
#[test]

View File

@ -112,7 +112,7 @@ impl Amount {
/// ```
#[cfg(feature = "alloc")]
pub fn from_btc(btc: f64) -> Result<Amount, ParseAmountError> {
Amount::from_float_in(btc, Denomination::Bitcoin)
Self::from_float_in(btc, Denomination::Bitcoin)
}
/// Converts from a value expressing a whole number of bitcoin to an [`Amount`].
@ -123,7 +123,7 @@ impl Amount {
/// 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(Amount::from_sat(amount)),
Some(amount) => Ok(Self::from_sat(amount)),
None => Err(OutOfRangeError { is_signed: false, is_greater_than_max: true }),
}
}
@ -152,19 +152,19 @@ impl Amount {
///
/// If the amount is too big, too precise or negative.
pub fn from_str_in(s: &str, denom: Denomination) -> Result<Amount, ParseAmountError> {
let (negative, satoshi) =
let (negative, sats) =
parse_signed_to_satoshi(s, denom).map_err(|error| error.convert(false))?;
if negative {
return Err(ParseAmountError(ParseAmountErrorInner::OutOfRange(
OutOfRangeError::negative(),
)));
}
if satoshi > Self::MAX.0 {
if sats > Self::MAX.0 {
return Err(ParseAmountError(ParseAmountErrorInner::OutOfRange(
OutOfRangeError::too_big(false),
)));
}
Ok(Amount::from_sat(satoshi))
Ok(Self::from_sat(sats))
}
/// Parses amounts with denomination suffix as produced by [`Self::to_string_with_denomination`]
@ -186,7 +186,7 @@ impl Amount {
/// ```
pub fn from_str_with_denomination(s: &str) -> Result<Amount, ParseError> {
let (amt, denom) = split_amount_and_denomination(s)?;
Amount::from_str_in(amt, denom).map_err(Into::into)
Self::from_str_in(amt, denom).map_err(Into::into)
}
/// Expresses this [`Amount`] as a floating-point value in the given [`Denomination`].
@ -234,7 +234,7 @@ impl Amount {
}
// This is inefficient, but the safest way to deal with this. The parsing logic is safe.
// Any performance-critical application should not be dealing with floats.
Amount::from_str_in(&value.to_string(), denom)
Self::from_str_in(&value.to_string(), denom)
}
/// Constructs a new object that implements [`fmt::Display`] in the given [`Denomination`].
@ -483,14 +483,14 @@ impl FromStr for Amount {
///
/// If the returned value would be zero or negative zero, then no denomination is required.
fn from_str(s: &str) -> Result<Self, Self::Err> {
let result = Amount::from_str_with_denomination(s);
let result = Self::from_str_with_denomination(s);
match result {
Err(ParseError(ParseErrorInner::MissingDenomination(_))) => {
let d = Amount::from_str_in(s, Denomination::Satoshi);
let d = Self::from_str_in(s, Denomination::Satoshi);
if d == Ok(Amount::ZERO) {
Ok(Amount::ZERO)
if d == Ok(Self::ZERO) {
Ok(Self::ZERO)
} else {
result
}
@ -509,7 +509,7 @@ impl TryFrom<SignedAmount> for Amount {
impl core::iter::Sum for Amount {
fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
let sats: u64 = iter.map(|amt| amt.0).sum();
Amount::from_sat(sats)
Self::from_sat(sats)
}
}
@ -519,7 +519,7 @@ impl<'a> core::iter::Sum<&'a Amount> for Amount {
I: Iterator<Item = &'a Amount>,
{
let sats: u64 = iter.map(|amt| amt.0).sum();
Amount::from_sat(sats)
Self::from_sat(sats)
}
}
@ -527,6 +527,6 @@ impl<'a> core::iter::Sum<&'a Amount> for Amount {
impl<'a> Arbitrary<'a> for Amount {
fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
let a = u64::arbitrary(u)?;
Ok(Amount(a))
Ok(Self(a))
}
}

View File

@ -188,9 +188,7 @@ impl ops::Div<FeeRate> for Amount {
/// This operation will panic if `fee_rate` is zero or the division results in overflow.
///
/// Note: This uses floor division. For ceiling division use [`Amount::checked_div_by_fee_rate_ceil`].
fn div(self, rhs: FeeRate) -> Self::Output {
self.checked_div_by_fee_rate_floor(rhs).unwrap()
}
fn div(self, rhs: FeeRate) -> Self::Output { self.checked_div_by_fee_rate_floor(rhs).unwrap() }
}
#[cfg(test)]
@ -199,7 +197,7 @@ mod tests {
#[test]
fn fee_rate_div_by_weight() {
let fee_rate = Amount::from_sat(329) / Weight::from_wu(381);
let fee_rate = Amount::from_sat_unchecked(329) / Weight::from_wu(381);
assert_eq!(fee_rate, FeeRate::from_sat_per_kwu(863));
}
@ -210,7 +208,7 @@ mod tests {
let fee_rate = FeeRate::from_sat_per_vb(2).unwrap();
let weight = Weight::from_vb(3).unwrap();
assert_eq!(fee_rate.fee_wu(weight).unwrap(), Amount::from_sat(6));
assert_eq!(fee_rate.fee_wu(weight).unwrap(), Amount::from_sat_unchecked(6));
}
#[test]
@ -219,7 +217,7 @@ mod tests {
assert!(fee_overflow.is_none());
let fee_rate = FeeRate::from_sat_per_vb(2).unwrap();
assert_eq!(fee_rate.fee_vb(3).unwrap(), Amount::from_sat(6));
assert_eq!(fee_rate.fee_vb(3).unwrap(), Amount::from_sat_unchecked(6));
}
#[test]
@ -229,7 +227,7 @@ mod tests {
.unwrap()
.checked_mul_by_weight(weight)
.expect("expected Amount");
assert_eq!(Amount::from_sat(100), fee);
assert_eq!(Amount::from_sat_unchecked(100), fee);
let fee = FeeRate::from_sat_per_kwu(10).checked_mul_by_weight(Weight::MAX);
assert!(fee.is_none());
@ -237,14 +235,14 @@ mod tests {
let weight = Weight::from_vb(3).unwrap();
let fee_rate = FeeRate::from_sat_per_vb(3).unwrap();
let fee = fee_rate.checked_mul_by_weight(weight).unwrap();
assert_eq!(Amount::from_sat(9), fee);
assert_eq!(Amount::from_sat_unchecked(9), fee);
let weight = Weight::from_wu(381);
let fee_rate = FeeRate::from_sat_per_kwu(864);
let fee = fee_rate.checked_mul_by_weight(weight).unwrap();
// 381 * 0.864 yields 329.18.
// The result is then rounded up to 330.
assert_eq!(fee, Amount::from_sat(330));
assert_eq!(fee, Amount::from_sat_unchecked(330));
}
#[test]
@ -252,7 +250,7 @@ mod tests {
fn multiply() {
let two = FeeRate::from_sat_per_vb(2).unwrap();
let three = Weight::from_vb(3).unwrap();
let six = Amount::from_sat(6);
let six = Amount::from_sat_unchecked(6);
assert_eq!(two * three, six);
}
@ -260,13 +258,13 @@ mod tests {
#[test]
fn amount_div_by_fee_rate() {
// Test exact division
let amount = Amount::from_sat(1000);
let amount = Amount::from_sat_unchecked(1000);
let fee_rate = FeeRate::from_sat_per_kwu(2);
let weight = amount / fee_rate;
assert_eq!(weight, Weight::from_wu(500_000));
// Test truncation behavior
let amount = Amount::from_sat(1000);
let amount = Amount::from_sat_unchecked(1000);
let fee_rate = FeeRate::from_sat_per_kwu(3);
let weight = amount / fee_rate;
// 1000 * 1000 = 1,000,000 msats