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:
commit
72e2b00721
|
@ -148,7 +148,7 @@ fn sighash_p2wpkh() {
|
||||||
let inp_idx = 0;
|
let inp_idx = 0;
|
||||||
//output value from the referenced vout:0 from the referenced tx:
|
//output value from the referenced vout:0 from the referenced tx:
|
||||||
//bitcoin-cli getrawtransaction 752d675b9cc0bd14e0bd23969effee0005ad6d7e550dcc832f0216c7ffd4e15c 3
|
//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:");
|
println!("\nsighash_p2wpkh:");
|
||||||
compute_sighash_p2wpkh(&raw_tx, inp_idx, ref_out_value);
|
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:
|
//For the witness transaction sighash computation, we need its referenced output's value from the original transaction:
|
||||||
//bitcoin-cli getrawtransaction 2845399a8cd7a52733f9f9d0e0b8b6c5d1c88aea4cee09f8d8fa762912b49e1b 3
|
//bitcoin-cli getrawtransaction 2845399a8cd7a52733f9f9d0e0b8b6c5d1c88aea4cee09f8d8fa762912b49e1b 3
|
||||||
//we need vout 0 value in sats:
|
//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:");
|
println!("\nsighash_p2wsh_multisig_2x2:");
|
||||||
compute_sighash_p2wsh(&raw_tx, 0, ref_out_value);
|
compute_sighash_p2wsh(&raw_tx, 0, ref_out_value);
|
||||||
|
|
|
@ -40,7 +40,7 @@ const UTXO_SCRIPT_PUBKEY: &str =
|
||||||
"5120be27fa8b1f5278faf82cab8da23e8761f8f9bd5d5ebebbb37e0e12a70d92dd16";
|
"5120be27fa8b1f5278faf82cab8da23e8761f8f9bd5d5ebebbb37e0e12a70d92dd16";
|
||||||
const UTXO_PUBKEY: &str = "a6ac32163539c16b6b5dbbca01b725b8e8acaa5f821ba42c80e7940062140d19";
|
const UTXO_PUBKEY: &str = "a6ac32163539c16b6b5dbbca01b725b8e8acaa5f821ba42c80e7940062140d19";
|
||||||
const UTXO_MASTER_FINGERPRINT: &str = "e61b318f";
|
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
|
// UTXO_1 will be used for spending example 1
|
||||||
const UTXO_1: P2trUtxo = P2trUtxo {
|
const UTXO_1: P2trUtxo = P2trUtxo {
|
||||||
|
@ -49,7 +49,7 @@ const UTXO_1: P2trUtxo = P2trUtxo {
|
||||||
script_pubkey: UTXO_SCRIPT_PUBKEY,
|
script_pubkey: UTXO_SCRIPT_PUBKEY,
|
||||||
pubkey: UTXO_PUBKEY,
|
pubkey: UTXO_PUBKEY,
|
||||||
master_fingerprint: UTXO_MASTER_FINGERPRINT,
|
master_fingerprint: UTXO_MASTER_FINGERPRINT,
|
||||||
amount_in_sats: Amount::FIFTY_BTC,
|
amount: Amount::FIFTY_BTC,
|
||||||
derivation_path: BIP86_DERIVATION_PATH,
|
derivation_path: BIP86_DERIVATION_PATH,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -60,7 +60,7 @@ const UTXO_2: P2trUtxo = P2trUtxo {
|
||||||
script_pubkey: UTXO_SCRIPT_PUBKEY,
|
script_pubkey: UTXO_SCRIPT_PUBKEY,
|
||||||
pubkey: UTXO_PUBKEY,
|
pubkey: UTXO_PUBKEY,
|
||||||
master_fingerprint: UTXO_MASTER_FINGERPRINT,
|
master_fingerprint: UTXO_MASTER_FINGERPRINT,
|
||||||
amount_in_sats: Amount::FIFTY_BTC,
|
amount: Amount::FIFTY_BTC,
|
||||||
derivation_path: BIP86_DERIVATION_PATH,
|
derivation_path: BIP86_DERIVATION_PATH,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -71,7 +71,7 @@ const UTXO_3: P2trUtxo = P2trUtxo {
|
||||||
script_pubkey: UTXO_SCRIPT_PUBKEY,
|
script_pubkey: UTXO_SCRIPT_PUBKEY,
|
||||||
pubkey: UTXO_PUBKEY,
|
pubkey: UTXO_PUBKEY,
|
||||||
master_fingerprint: UTXO_MASTER_FINGERPRINT,
|
master_fingerprint: UTXO_MASTER_FINGERPRINT,
|
||||||
amount_in_sats: Amount::FIFTY_BTC,
|
amount: Amount::FIFTY_BTC,
|
||||||
derivation_path: BIP86_DERIVATION_PATH,
|
derivation_path: BIP86_DERIVATION_PATH,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -106,11 +106,11 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
let change_address = "bcrt1pz449kexzydh2kaypatup5ultru3ej284t6eguhnkn6wkhswt0l7q3a7j76"
|
let change_address = "bcrt1pz449kexzydh2kaypatup5ultru3ej284t6eguhnkn6wkhswt0l7q3a7j76"
|
||||||
.parse::<Address<_>>()?
|
.parse::<Address<_>>()?
|
||||||
.require_network(Network::Regtest)?;
|
.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
|
let change_amount = UTXO_1
|
||||||
.amount_in_sats
|
.amount
|
||||||
.checked_sub(amount_to_send_in_sats)
|
.checked_sub(amount_to_send)
|
||||||
.and_then(|x| x.checked_sub(ABSOLUTE_FEES_IN_SATS))
|
.and_then(|x| x.checked_sub(ABSOLUTE_FEES))
|
||||||
.ok_or("fees more than input amount!")?;
|
.ok_or("fees more than input amount!")?;
|
||||||
|
|
||||||
let tx_hex_string = encode::serialize_hex(&generate_bip86_key_spend_tx(
|
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
|
// Set these fields with valid data for the UTXO from step 5 above
|
||||||
UTXO_1,
|
UTXO_1,
|
||||||
vec![
|
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() },
|
TxOut { value: change_amount, script_pubkey: change_address.script_pubkey() },
|
||||||
],
|
],
|
||||||
)?);
|
)?);
|
||||||
|
@ -215,7 +215,7 @@ struct P2trUtxo<'a> {
|
||||||
script_pubkey: &'a str,
|
script_pubkey: &'a str,
|
||||||
pubkey: &'a str,
|
pubkey: &'a str,
|
||||||
master_fingerprint: &'a str,
|
master_fingerprint: &'a str,
|
||||||
amount_in_sats: Amount,
|
amount: Amount,
|
||||||
derivation_path: &'a str,
|
derivation_path: &'a str,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -226,7 +226,7 @@ fn generate_bip86_key_spend_tx(
|
||||||
input_utxo: P2trUtxo,
|
input_utxo: P2trUtxo,
|
||||||
outputs: Vec<TxOut>,
|
outputs: Vec<TxOut>,
|
||||||
) -> Result<Transaction, Box<dyn std::error::Error>> {
|
) -> 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>()?;
|
let input_pubkey = input_utxo.pubkey.parse::<XOnlyPublicKey>()?;
|
||||||
|
|
||||||
// CREATOR + UPDATER
|
// CREATOR + UPDATER
|
||||||
|
@ -274,7 +274,7 @@ fn generate_bip86_key_spend_tx(
|
||||||
let mut input_txouts = Vec::<TxOut>::new();
|
let mut input_txouts = Vec::<TxOut>::new();
|
||||||
for input in [&input_utxo].iter() {
|
for input in [&input_utxo].iter() {
|
||||||
input_txouts.push(TxOut {
|
input_txouts.push(TxOut {
|
||||||
value: input.amount_in_sats,
|
value: input.amount,
|
||||||
script_pubkey: ScriptBuf::from_hex(input.script_pubkey)?,
|
script_pubkey: ScriptBuf::from_hex(input.script_pubkey)?,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -412,7 +412,7 @@ impl BenefactorWallet {
|
||||||
taproot_spend_info.internal_key(),
|
taproot_spend_info.internal_key(),
|
||||||
taproot_spend_info.merkle_root(),
|
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
|
// Spend a normal BIP86-like output as an input in our inheritance funding transaction
|
||||||
let tx = generate_bip86_key_spend_tx(
|
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 mut psbt = self.next_psbt.clone().expect("should have next_psbt");
|
||||||
let input = &mut psbt.inputs[0];
|
let input = &mut psbt.inputs[0];
|
||||||
let input_value = input.witness_utxo.as_ref().unwrap().value;
|
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
|
// 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.
|
// 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.lock_time = lock_time;
|
||||||
psbt.unsigned_tx.output = vec![TxOut {
|
psbt.unsigned_tx.output = vec![TxOut {
|
||||||
script_pubkey: to_address.script_pubkey(),
|
script_pubkey: to_address.script_pubkey(),
|
||||||
value: input_value - ABSOLUTE_FEES_IN_SATS,
|
value: input_value - ABSOLUTE_FEES,
|
||||||
}];
|
}];
|
||||||
psbt.outputs = vec![Output::default()];
|
psbt.outputs = vec![Output::default()];
|
||||||
let unsigned_tx = psbt.unsigned_tx.clone();
|
let unsigned_tx = psbt.unsigned_tx.clone();
|
||||||
|
|
|
@ -112,10 +112,7 @@ fn bitcoin_genesis_tx(params: &Params) -> Transaction {
|
||||||
witness: Witness::default(),
|
witness: Witness::default(),
|
||||||
});
|
});
|
||||||
|
|
||||||
ret.output.push(TxOut {
|
ret.output.push(TxOut { value: Amount::FIFTY_BTC, script_pubkey: out_script });
|
||||||
value: Amount::FIFTY_BTC,
|
|
||||||
script_pubkey: out_script,
|
|
||||||
});
|
|
||||||
|
|
||||||
// end
|
// end
|
||||||
ret
|
ret
|
||||||
|
|
|
@ -15,7 +15,7 @@ use crate::opcodes::{self, Opcode};
|
||||||
use crate::policy::{DUST_RELAY_TX_FEE, MAX_OP_RETURN_RELAY};
|
use crate::policy::{DUST_RELAY_TX_FEE, MAX_OP_RETURN_RELAY};
|
||||||
use crate::prelude::{sink, DisplayHex, String, ToString};
|
use crate::prelude::{sink, DisplayHex, String, ToString};
|
||||||
use crate::taproot::{LeafVersion, TapLeafHash, TapLeafHashExt as _};
|
use crate::taproot::{LeafVersion, TapLeafHash, TapLeafHashExt as _};
|
||||||
use crate::FeeRate;
|
use crate::{Amount, FeeRate};
|
||||||
|
|
||||||
#[rustfmt::skip] // Keep public re-exports separate.
|
#[rustfmt::skip] // Keep public re-exports separate.
|
||||||
#[doc(inline)]
|
#[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
|
/// Returns the minimum value an output with this script should have in order to be
|
||||||
/// broadcastable on today’s Bitcoin network.
|
/// broadcastable on today’s Bitcoin network.
|
||||||
#[deprecated(since = "0.32.0", note = "use `minimal_non_dust` etc. instead")]
|
#[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
|
/// Returns the minimum value an output with this script should have in order to be
|
||||||
/// broadcastable on today's Bitcoin network.
|
/// 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`].
|
/// To use a custom value, use [`minimal_non_dust_custom`].
|
||||||
///
|
///
|
||||||
/// [`minimal_non_dust_custom`]: Script::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())
|
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`].
|
/// To use the default Bitcoin Core value, use [`minimal_non_dust`].
|
||||||
///
|
///
|
||||||
/// [`minimal_non_dust`]: Script::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)
|
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! {
|
crate::internal_macros::define_extension_trait! {
|
||||||
pub(crate) trait ScriptExtPriv impl for Script {
|
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
|
// 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.
|
// otherwise allow users to create transactions which likely can never be broadcast/confirmed.
|
||||||
let sats = dust_relay_fee
|
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.
|
// 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.
|
// 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 {
|
fn count_sigops_internal(&self, accurate: bool) -> usize {
|
||||||
|
|
|
@ -9,7 +9,7 @@ use crate::address::script_pubkey::{
|
||||||
};
|
};
|
||||||
use crate::consensus::encode::{deserialize, serialize};
|
use crate::consensus::encode::{deserialize, serialize};
|
||||||
use crate::crypto::key::{PublicKey, XOnlyPublicKey};
|
use crate::crypto::key::{PublicKey, XOnlyPublicKey};
|
||||||
use crate::FeeRate;
|
use crate::{Amount, FeeRate};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
|
@ -676,7 +676,7 @@ fn bitcoinconsensus() {
|
||||||
let spent_bytes = hex!("0020701a8d401c84fb13e6baf169d59684e17abd9fa216c8cc5b9fc63d622ff8c58d");
|
let spent_bytes = hex!("0020701a8d401c84fb13e6baf169d59684e17abd9fa216c8cc5b9fc63d622ff8c58d");
|
||||||
let spent = Script::from_bytes(&spent_bytes);
|
let spent = Script::from_bytes(&spent_bytes);
|
||||||
let spending = hex!("010000000001011f97548fbbe7a0db7588a66e18d803d0089315aa7d4cc28360b6ec50ef36718a0100000000ffffffff02df1776000000000017a9146c002a686959067f4866b8fb493ad7970290ab728757d29f0000000000220020701a8d401c84fb13e6baf169d59684e17abd9fa216c8cc5b9fc63d622ff8c58d04004730440220565d170eed95ff95027a69b313758450ba84a01224e1f7f130dda46e94d13f8602207bdd20e307f062594022f12ed5017bbf4a055a06aea91c10110a0e3bb23117fc014730440220647d2dc5b15f60bc37dc42618a370b2a1490293f9e5c8464f53ec4fe1dfe067302203598773895b4b16d37485cbe21b337f4e4b650739880098c592553add7dd4355016952210375e00eb72e29da82b89367947f29ef34afb75e8654f6ea368e0acdfd92976b7c2103a1b26313f430c4b15bb1fdce663207659d8cac749a0e53d70eff01874496feff2103c96d495bfdd5ba4145e3e046fee45e84a8a48ad05bd8dbb395c011a32cf9f88053ae00000000");
|
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]
|
#[test]
|
||||||
|
@ -685,10 +685,10 @@ fn default_dust_value() {
|
||||||
// well-known scriptPubKey types.
|
// well-known scriptPubKey types.
|
||||||
let script_p2wpkh = Builder::new().push_int_unchecked(0).push_slice([42; 20]).into_script();
|
let script_p2wpkh = Builder::new().push_int_unchecked(0).push_slice([42; 20]).into_script();
|
||||||
assert!(script_p2wpkh.is_p2wpkh());
|
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!(
|
assert_eq!(
|
||||||
script_p2wpkh.minimal_non_dust_custom(FeeRate::from_sat_per_vb_unchecked(6)),
|
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()
|
let script_p2pkh = Builder::new()
|
||||||
|
@ -699,10 +699,10 @@ fn default_dust_value() {
|
||||||
.push_opcode(OP_CHECKSIG)
|
.push_opcode(OP_CHECKSIG)
|
||||||
.into_script();
|
.into_script();
|
||||||
assert!(script_p2pkh.is_p2pkh());
|
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!(
|
assert_eq!(
|
||||||
script_p2pkh.minimal_non_dust_custom(FeeRate::from_sat_per_vb_unchecked(6)),
|
script_p2pkh.minimal_non_dust_custom(FeeRate::from_sat_per_vb_unchecked(6)),
|
||||||
crate::Amount::from_sat_unchecked(1092)
|
Amount::from_sat_unchecked(1092)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -134,7 +134,7 @@ define_extension_trait! {
|
||||||
fn verify(
|
fn verify(
|
||||||
&self,
|
&self,
|
||||||
index: usize,
|
index: usize,
|
||||||
amount: crate::Amount,
|
amount: Amount,
|
||||||
spending_tx: &[u8],
|
spending_tx: &[u8],
|
||||||
) -> Result<(), BitcoinconsensusError> {
|
) -> Result<(), BitcoinconsensusError> {
|
||||||
verify_script(self, index, amount, spending_tx)
|
verify_script(self, index, amount, spending_tx)
|
||||||
|
@ -153,7 +153,7 @@ define_extension_trait! {
|
||||||
fn verify_with_flags(
|
fn verify_with_flags(
|
||||||
&self,
|
&self,
|
||||||
index: usize,
|
index: usize,
|
||||||
amount: crate::Amount,
|
amount: Amount,
|
||||||
spending_tx: &[u8],
|
spending_tx: &[u8],
|
||||||
flags: impl Into<u32>,
|
flags: impl Into<u32>,
|
||||||
) -> Result<(), BitcoinconsensusError> {
|
) -> Result<(), BitcoinconsensusError> {
|
||||||
|
|
|
@ -109,7 +109,7 @@ impl SignedAmount {
|
||||||
/// ```
|
/// ```
|
||||||
#[cfg(feature = "alloc")]
|
#[cfg(feature = "alloc")]
|
||||||
pub fn from_btc(btc: f64) -> Result<SignedAmount, ParseAmountError> {
|
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`].
|
/// 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.
|
/// per bitcoin overflows an `i64` type.
|
||||||
pub fn from_int_btc<T: Into<i64>>(whole_bitcoin: T) -> Result<SignedAmount, OutOfRangeError> {
|
pub fn from_int_btc<T: Into<i64>>(whole_bitcoin: T) -> Result<SignedAmount, OutOfRangeError> {
|
||||||
match whole_bitcoin.into().checked_mul(100_000_000) {
|
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 }),
|
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> {
|
pub fn from_str_with_denomination(s: &str) -> Result<SignedAmount, ParseError> {
|
||||||
let (amt, denom) = split_amount_and_denomination(s)?;
|
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`].
|
/// Expresses this [`SignedAmount`] as a floating-point value in the given [`Denomination`].
|
||||||
|
@ -228,7 +228,7 @@ impl SignedAmount {
|
||||||
) -> Result<SignedAmount, ParseAmountError> {
|
) -> Result<SignedAmount, ParseAmountError> {
|
||||||
// This is inefficient, but the safest way to deal with this. The parsing logic is safe.
|
// 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.
|
// 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`].
|
/// 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.
|
/// If the returned value would be zero or negative zero, then no denomination is required.
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
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 {
|
match result {
|
||||||
Err(ParseError(ParseErrorInner::MissingDenomination(_))) => {
|
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) {
|
if d == Ok(Self::ZERO) {
|
||||||
Ok(SignedAmount::ZERO)
|
Ok(Self::ZERO)
|
||||||
} else {
|
} else {
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
@ -568,14 +568,14 @@ impl FromStr for SignedAmount {
|
||||||
impl From<Amount> for SignedAmount {
|
impl From<Amount> for SignedAmount {
|
||||||
fn from(value: Amount) -> Self {
|
fn from(value: Amount) -> Self {
|
||||||
let v = value.to_sat() as i64; // Cast ok, signed amount and amount share positive range.
|
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 {
|
impl core::iter::Sum for SignedAmount {
|
||||||
fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
|
fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
|
||||||
let sats: i64 = iter.map(|amt| amt.0).sum();
|
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>,
|
I: Iterator<Item = &'a SignedAmount>,
|
||||||
{
|
{
|
||||||
let sats: i64 = iter.map(|amt| amt.0).sum();
|
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 {
|
impl<'a> Arbitrary<'a> for SignedAmount {
|
||||||
fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
|
fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
|
||||||
let s = i64::arbitrary(u)?;
|
let s = i64::arbitrary(u)?;
|
||||||
Ok(SignedAmount(s))
|
Ok(Self(s))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,10 +22,7 @@ fn sanity_check() {
|
||||||
let ssat = SignedAmount::from_sat;
|
let ssat = SignedAmount::from_sat;
|
||||||
|
|
||||||
assert_eq!(ssat(-100).abs(), ssat(100));
|
assert_eq!(ssat(-100).abs(), ssat(100));
|
||||||
assert_eq!(
|
assert_eq!(ssat(i64::MIN + 1).checked_abs().unwrap(), ssat(i64::MAX));
|
||||||
ssat(i64::MIN + 1).checked_abs().unwrap(),
|
|
||||||
ssat(i64::MAX)
|
|
||||||
);
|
|
||||||
assert_eq!(ssat(-100).signum(), -1);
|
assert_eq!(ssat(-100).signum(), -1);
|
||||||
assert_eq!(ssat(0).signum(), 0);
|
assert_eq!(ssat(0).signum(), 0);
|
||||||
assert_eq!(ssat(100).signum(), 1);
|
assert_eq!(ssat(100).signum(), 1);
|
||||||
|
@ -36,14 +33,8 @@ fn sanity_check() {
|
||||||
|
|
||||||
#[cfg(feature = "alloc")]
|
#[cfg(feature = "alloc")]
|
||||||
{
|
{
|
||||||
assert_eq!(
|
assert_eq!(Amount::from_float_in(0_f64, Denomination::Bitcoin).unwrap(), sat(0));
|
||||||
Amount::from_float_in(0_f64, Denomination::Bitcoin).unwrap(),
|
assert_eq!(Amount::from_float_in(2_f64, Denomination::Bitcoin).unwrap(), sat(200_000_000));
|
||||||
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());
|
assert!(Amount::from_float_in(-100_f64, Denomination::Bitcoin).is_err());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -151,7 +142,7 @@ fn mul_div() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn neg() {
|
fn neg() {
|
||||||
let amount = -SignedAmount::from_sat(2);
|
let amount = -SignedAmount::from_sat_unchecked(2);
|
||||||
assert_eq!(amount.to_sat(), -2);
|
assert_eq!(amount.to_sat(), -2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -185,14 +176,8 @@ fn unchecked_arithmetic() {
|
||||||
let sat = Amount::from_sat;
|
let sat = Amount::from_sat;
|
||||||
let ssat = SignedAmount::from_sat;
|
let ssat = SignedAmount::from_sat;
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(ssat(10).unchecked_add(ssat(20)), ssat(30));
|
||||||
ssat(10).unchecked_add(ssat(20)),
|
assert_eq!(ssat(50).unchecked_sub(ssat(10)), ssat(40));
|
||||||
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(5).unchecked_add(sat(7)), sat(12));
|
||||||
assert_eq!(sat(10).unchecked_sub(sat(7)), sat(3));
|
assert_eq!(sat(10).unchecked_sub(sat(7)), sat(3));
|
||||||
}
|
}
|
||||||
|
@ -201,10 +186,7 @@ fn unchecked_arithmetic() {
|
||||||
fn positive_sub() {
|
fn positive_sub() {
|
||||||
let ssat = SignedAmount::from_sat;
|
let ssat = SignedAmount::from_sat;
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(ssat(10).positive_sub(ssat(7)).unwrap(), ssat(3));
|
||||||
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(-7)).is_none());
|
assert!(ssat(10).positive_sub(ssat(-7)).is_none());
|
||||||
assert!(ssat(10).positive_sub(ssat(11)).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")]
|
#[cfg(feature = "alloc")]
|
||||||
#[test]
|
#[test]
|
||||||
fn amount_checked_div_by_fee_rate() {
|
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);
|
let fee_rate = FeeRate::from_sat_per_kwu(2);
|
||||||
|
|
||||||
// Test floor division
|
// 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
|
assert_eq!(weight, Weight::from_wu(500_000)); // Same result for exact division
|
||||||
|
|
||||||
// Test truncation behavior
|
// 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 fee_rate = FeeRate::from_sat_per_kwu(3);
|
||||||
let floor_weight = amount.checked_div_by_fee_rate_floor(fee_rate).unwrap();
|
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();
|
let ceil_weight = amount.checked_div_by_fee_rate_ceil(fee_rate).unwrap();
|
||||||
|
@ -589,15 +571,13 @@ check_format_non_negative_show_denom! {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn unsigned_signed_conversion() {
|
fn unsigned_signed_conversion() {
|
||||||
let sa = SignedAmount::from_sat;
|
let ssat = SignedAmount::from_sat;
|
||||||
let ua = Amount::from_sat;
|
let sat = Amount::from_sat;
|
||||||
let max_sats: u64 = Amount::MAX.to_sat();
|
let max_sats: u64 = Amount::MAX.to_sat();
|
||||||
|
|
||||||
assert_eq!(ua(max_sats).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!(sa(max_sats as i64).to_unsigned(), Ok(ua(max_sats)));
|
assert_eq!(ssat(max_sats as i64).to_unsigned().unwrap().to_signed(), ssat(max_sats as i64));
|
||||||
|
|
||||||
assert_eq!(sa(max_sats as i64).to_unsigned().unwrap().to_signed(), sa(max_sats as i64));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -112,7 +112,7 @@ impl Amount {
|
||||||
/// ```
|
/// ```
|
||||||
#[cfg(feature = "alloc")]
|
#[cfg(feature = "alloc")]
|
||||||
pub fn from_btc(btc: f64) -> Result<Amount, ParseAmountError> {
|
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`].
|
/// 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.
|
/// per bitcoin overflows a `u64` type.
|
||||||
pub fn from_int_btc<T: Into<u64>>(whole_bitcoin: T) -> Result<Amount, OutOfRangeError> {
|
pub fn from_int_btc<T: Into<u64>>(whole_bitcoin: T) -> Result<Amount, OutOfRangeError> {
|
||||||
match whole_bitcoin.into().checked_mul(100_000_000) {
|
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 }),
|
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.
|
/// If the amount is too big, too precise or negative.
|
||||||
pub fn from_str_in(s: &str, denom: Denomination) -> Result<Amount, ParseAmountError> {
|
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))?;
|
parse_signed_to_satoshi(s, denom).map_err(|error| error.convert(false))?;
|
||||||
if negative {
|
if negative {
|
||||||
return Err(ParseAmountError(ParseAmountErrorInner::OutOfRange(
|
return Err(ParseAmountError(ParseAmountErrorInner::OutOfRange(
|
||||||
OutOfRangeError::negative(),
|
OutOfRangeError::negative(),
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
if satoshi > Self::MAX.0 {
|
if sats > Self::MAX.0 {
|
||||||
return Err(ParseAmountError(ParseAmountErrorInner::OutOfRange(
|
return Err(ParseAmountError(ParseAmountErrorInner::OutOfRange(
|
||||||
OutOfRangeError::too_big(false),
|
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`]
|
/// 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> {
|
pub fn from_str_with_denomination(s: &str) -> Result<Amount, ParseError> {
|
||||||
let (amt, denom) = split_amount_and_denomination(s)?;
|
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`].
|
/// 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.
|
// 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.
|
// 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`].
|
/// 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.
|
/// If the returned value would be zero or negative zero, then no denomination is required.
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
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 {
|
match result {
|
||||||
Err(ParseError(ParseErrorInner::MissingDenomination(_))) => {
|
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) {
|
if d == Ok(Self::ZERO) {
|
||||||
Ok(Amount::ZERO)
|
Ok(Self::ZERO)
|
||||||
} else {
|
} else {
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
@ -509,7 +509,7 @@ impl TryFrom<SignedAmount> for Amount {
|
||||||
impl core::iter::Sum for Amount {
|
impl core::iter::Sum for Amount {
|
||||||
fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
|
fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
|
||||||
let sats: u64 = iter.map(|amt| amt.0).sum();
|
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>,
|
I: Iterator<Item = &'a Amount>,
|
||||||
{
|
{
|
||||||
let sats: u64 = iter.map(|amt| amt.0).sum();
|
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 {
|
impl<'a> Arbitrary<'a> for Amount {
|
||||||
fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
|
fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
|
||||||
let a = u64::arbitrary(u)?;
|
let a = u64::arbitrary(u)?;
|
||||||
Ok(Amount(a))
|
Ok(Self(a))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.
|
/// 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`].
|
/// Note: This uses floor division. For ceiling division use [`Amount::checked_div_by_fee_rate_ceil`].
|
||||||
fn div(self, rhs: FeeRate) -> Self::Output {
|
fn div(self, rhs: FeeRate) -> Self::Output { self.checked_div_by_fee_rate_floor(rhs).unwrap() }
|
||||||
self.checked_div_by_fee_rate_floor(rhs).unwrap()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -199,7 +197,7 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn fee_rate_div_by_weight() {
|
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));
|
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 fee_rate = FeeRate::from_sat_per_vb(2).unwrap();
|
||||||
let weight = Weight::from_vb(3).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]
|
#[test]
|
||||||
|
@ -219,7 +217,7 @@ mod tests {
|
||||||
assert!(fee_overflow.is_none());
|
assert!(fee_overflow.is_none());
|
||||||
|
|
||||||
let fee_rate = FeeRate::from_sat_per_vb(2).unwrap();
|
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]
|
#[test]
|
||||||
|
@ -229,7 +227,7 @@ mod tests {
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.checked_mul_by_weight(weight)
|
.checked_mul_by_weight(weight)
|
||||||
.expect("expected Amount");
|
.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);
|
let fee = FeeRate::from_sat_per_kwu(10).checked_mul_by_weight(Weight::MAX);
|
||||||
assert!(fee.is_none());
|
assert!(fee.is_none());
|
||||||
|
@ -237,14 +235,14 @@ mod tests {
|
||||||
let weight = Weight::from_vb(3).unwrap();
|
let weight = Weight::from_vb(3).unwrap();
|
||||||
let fee_rate = FeeRate::from_sat_per_vb(3).unwrap();
|
let fee_rate = FeeRate::from_sat_per_vb(3).unwrap();
|
||||||
let fee = fee_rate.checked_mul_by_weight(weight).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 weight = Weight::from_wu(381);
|
||||||
let fee_rate = FeeRate::from_sat_per_kwu(864);
|
let fee_rate = FeeRate::from_sat_per_kwu(864);
|
||||||
let fee = fee_rate.checked_mul_by_weight(weight).unwrap();
|
let fee = fee_rate.checked_mul_by_weight(weight).unwrap();
|
||||||
// 381 * 0.864 yields 329.18.
|
// 381 * 0.864 yields 329.18.
|
||||||
// The result is then rounded up to 330.
|
// The result is then rounded up to 330.
|
||||||
assert_eq!(fee, Amount::from_sat(330));
|
assert_eq!(fee, Amount::from_sat_unchecked(330));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -252,7 +250,7 @@ mod tests {
|
||||||
fn multiply() {
|
fn multiply() {
|
||||||
let two = FeeRate::from_sat_per_vb(2).unwrap();
|
let two = FeeRate::from_sat_per_vb(2).unwrap();
|
||||||
let three = Weight::from_vb(3).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);
|
assert_eq!(two * three, six);
|
||||||
}
|
}
|
||||||
|
@ -260,13 +258,13 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn amount_div_by_fee_rate() {
|
fn amount_div_by_fee_rate() {
|
||||||
// Test exact division
|
// 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 fee_rate = FeeRate::from_sat_per_kwu(2);
|
||||||
let weight = amount / fee_rate;
|
let weight = amount / fee_rate;
|
||||||
assert_eq!(weight, Weight::from_wu(500_000));
|
assert_eq!(weight, Weight::from_wu(500_000));
|
||||||
|
|
||||||
// Test truncation behavior
|
// 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 fee_rate = FeeRate::from_sat_per_kwu(3);
|
||||||
let weight = amount / fee_rate;
|
let weight = amount / fee_rate;
|
||||||
// 1000 * 1000 = 1,000,000 msats
|
// 1000 * 1000 = 1,000,000 msats
|
||||||
|
|
Loading…
Reference in New Issue