Add `Weight` and `FeeRate` newtypes

Use of general-purpose integers is often error-prone and annoying. We're
working towards improving it by introducing newtypes.

This adds newtypes for weight and fee rate to make fee computation
easier and more readable. Note however that this dosn't change the type
for individual parts of the transaction since computing the total weight
is not as simple as summing them up and we want to avoid such confusion.

Part of #630
This commit is contained in:
Martin Habovstiak 2023-02-06 21:01:48 +01:00
parent 69688b6487
commit 70cf4515db
7 changed files with 363 additions and 23 deletions

View File

@ -8,7 +8,7 @@ fn do_test(data: &[u8]) {
let ser = bitcoin::consensus::encode::serialize(&tx); let ser = bitcoin::consensus::encode::serialize(&tx);
assert_eq!(&ser[..], data); assert_eq!(&ser[..], data);
let len = ser.len(); let len = ser.len();
let calculated_weight = tx.weight(); let calculated_weight = tx.weight().to_wu() as usize;
for input in &mut tx.input { for input in &mut tx.input {
input.witness = bitcoin::blockdata::witness::Witness::default(); input.witness = bitcoin::blockdata::witness::Witness::default();
} }

View File

@ -19,12 +19,12 @@ use crate::hashes::{Hash, HashEngine};
use crate::hash_types::{Wtxid, TxMerkleNode, WitnessMerkleNode, WitnessCommitment}; use crate::hash_types::{Wtxid, TxMerkleNode, WitnessMerkleNode, WitnessCommitment};
use crate::consensus::{encode, Encodable, Decodable}; use crate::consensus::{encode, Encodable, Decodable};
use crate::blockdata::transaction::Transaction; use crate::blockdata::transaction::Transaction;
use crate::blockdata::constants::WITNESS_SCALE_FACTOR;
use crate::blockdata::script; use crate::blockdata::script;
use crate::pow::{CompactTarget, Target, Work}; use crate::pow::{CompactTarget, Target, Work};
use crate::VarInt; use crate::VarInt;
use crate::internal_macros::impl_consensus_encoding; use crate::internal_macros::impl_consensus_encoding;
use crate::io; use crate::io;
use super::Weight;
pub use crate::hash_types::BlockHash; pub use crate::hash_types::BlockHash;
@ -302,9 +302,9 @@ impl Block {
} }
/// Returns the weight of the block. /// Returns the weight of the block.
pub fn weight(&self) -> usize { pub fn weight(&self) -> Weight {
let base_weight = WITNESS_SCALE_FACTOR * self.base_size(); let base_weight = Weight::from_non_witness_data_size(self.base_size() as u64);
let txs_weight: usize = self.txdata.iter().map(Transaction::weight).sum(); let txs_weight: Weight = self.txdata.iter().map(Transaction::weight).sum();
base_weight + txs_weight base_weight + txs_weight
} }
@ -470,7 +470,7 @@ mod tests {
assert_eq!(real_decode.size(), some_block.len()); assert_eq!(real_decode.size(), some_block.len());
assert_eq!(real_decode.strippedsize(), some_block.len()); assert_eq!(real_decode.strippedsize(), some_block.len());
assert_eq!(real_decode.weight(), some_block.len() * 4); assert_eq!(real_decode.weight(), Weight::from_non_witness_data_size(some_block.len() as u64));
// should be also ok for a non-witness block as commitment is optional in that case // should be also ok for a non-witness block as commitment is optional in that case
assert!(real_decode.check_witness_commitment()); assert!(real_decode.check_witness_commitment());
@ -505,7 +505,7 @@ mod tests {
assert_eq!(real_decode.size(), segwit_block.len()); assert_eq!(real_decode.size(), segwit_block.len());
assert_eq!(real_decode.strippedsize(), 4283); assert_eq!(real_decode.strippedsize(), 4283);
assert_eq!(real_decode.weight(), 17168); assert_eq!(real_decode.weight(), Weight::from_wu(17168));
assert!(real_decode.check_witness_commitment()); assert!(real_decode.check_witness_commitment());

View File

@ -0,0 +1,136 @@
//! Implements `FeeRate` and assoctiated features.
use core::fmt;
use core::ops::{Mul, Div};
use crate::Amount;
use super::Weight;
/// Represents fee rate.
///
/// This is an integer newtype representing fee rate in `sat/kwu`. It provides protection against mixing
/// up the types as well as basic formatting features.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(crate = "actual_serde"))]
#[cfg_attr(feature = "serde", serde(transparent))]
pub struct FeeRate(u64);
impl FeeRate {
/// 0 sat/kwu.
///
/// Equivalent to [`MIN`](Self::MIN), may better express intent in some contexts.
pub const ZERO: FeeRate = FeeRate(0);
/// Minimum possible value (0 sat/kwu).
///
/// Equivalent to [`ZERO`](Self::ZERO), may better express intent in some contexts.
pub const MIN: FeeRate = FeeRate(u64::min_value());
/// Maximum possible value.
pub const MAX: FeeRate = FeeRate(u64::max_value());
/// Minimum fee rate required to broadcast a transaction.
///
/// The value matches the default Bitcoin Core policy at the time of library release.
pub const BROADCAST_MIN: FeeRate = FeeRate::from_sat_per_vb_unchecked(1);
/// Fee rate used to compute dust amount.
pub const DUST: FeeRate = FeeRate::from_sat_per_vb_unchecked(3);
/// Constructs `FeeRate` from satoshis per 1000 weight units.
pub const fn from_sat_per_kwu(sat_kwu: u64) -> Self {
FeeRate(sat_kwu)
}
/// Constructs `FeeRate` from satoshis per virtual bytes.
///
/// # Errors
///
/// Returns `None` on arithmetic overflow.
pub fn from_sat_per_vb(sat_vb: u64) -> Option<Self> {
// 1 vb == 4 wu
// 1 sat/vb == 1/4 sat/wu
// sat_vb sat/vb * 1000 / 4 == sat/kwu
Some(FeeRate(sat_vb.checked_mul(1000 / 4)?))
}
/// Constructs `FeeRate` from satoshis per virtual bytes without overflow check.
pub const fn from_sat_per_vb_unchecked(sat_vb: u64) -> Self {
FeeRate(sat_vb * (1000 / 4))
}
/// Returns raw fee rate.
///
/// Can be used instead of `into()` to avoid inference issues.
pub const fn to_sat_per_kwu(self) -> u64 {
self.0
}
/// Converts to sat/vB rounding down.
pub const fn to_sat_per_vb_floor(self) -> u64 {
self.0 / (1000 / 4)
}
/// Converts to sat/vB rounding up.
pub const fn to_sat_per_vb_ceil(self) -> u64 {
(self.0 + (1000 / 4 - 1)) / (1000 / 4)
}
/// Checked multiplication.
///
/// Computes `self * rhs` returning `None` if overflow occurred.
pub fn checked_mul(self, rhs: u64) -> Option<Self> {
self.0.checked_mul(rhs).map(Self)
}
/// Checked division.
///
/// Computes `self / rhs` returning `None` if `rhs == 0`.
pub fn checked_div(self, rhs: u64) -> Option<Self> {
self.0.checked_div(rhs).map(Self)
}
}
/// Alternative will display the unit.
impl fmt::Display for FeeRate {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if f.alternate() {
write!(f, "{} sat/kwu", self.0)
} else {
fmt::Display::fmt(&self.0, f)
}
}
}
impl From<FeeRate> for u64 {
fn from(value: FeeRate) -> Self {
value.to_sat_per_kwu()
}
}
/// Computes ceiling so that fee computation is conservative.
impl Mul<FeeRate> for Weight {
type Output = Amount;
fn mul(self, rhs: FeeRate) -> Self::Output {
Amount::from_sat((rhs.to_sat_per_kwu() * self.to_wu() + 999) / 1000)
}
}
impl Mul<Weight> for FeeRate {
type Output = Amount;
fn mul(self, rhs: Weight) -> Self::Output {
rhs * self
}
}
impl Div<Weight> for Amount {
type Output = FeeRate;
fn div(self, rhs: Weight) -> Self::Output {
FeeRate(self.to_sat() * 1000 / rhs.to_wu())
}
}
crate::parse::impl_parse_str_through_int!(FeeRate);

View File

@ -14,3 +14,8 @@ pub mod script;
pub mod transaction; pub mod transaction;
pub mod block; pub mod block;
pub mod witness; pub mod witness;
pub mod weight;
pub mod fee_rate;
pub use weight::Weight;
pub use fee_rate::FeeRate;

View File

@ -34,6 +34,7 @@ use crate::hash_types::{Sighash, Txid, Wtxid};
use crate::VarInt; use crate::VarInt;
use crate::internal_macros::impl_consensus_encoding; use crate::internal_macros::impl_consensus_encoding;
use crate::parse::impl_parse_str_through_int; use crate::parse::impl_parse_str_through_int;
use super::Weight;
#[cfg(doc)] #[cfg(doc)]
use crate::sighash::{EcdsaSighashType, SchnorrSighashType}; use crate::sighash::{EcdsaSighashType, SchnorrSighashType};
@ -839,8 +840,8 @@ impl Transaction {
/// API. The unsigned transaction encoded within PSBT is always a non-segwit transaction /// API. The unsigned transaction encoded within PSBT is always a non-segwit transaction
/// and can therefore avoid this ambiguity. /// and can therefore avoid this ambiguity.
#[inline] #[inline]
pub fn weight(&self) -> usize { pub fn weight(&self) -> Weight {
self.scaled_size(WITNESS_SCALE_FACTOR) Weight::from_wu(self.scaled_size(WITNESS_SCALE_FACTOR) as u64)
} }
/// Returns the regular byte-wise consensus-serialized size of this transaction. /// Returns the regular byte-wise consensus-serialized size of this transaction.
@ -860,8 +861,8 @@ impl Transaction {
/// [`policy`]: ../policy/mod.rs.html /// [`policy`]: ../policy/mod.rs.html
#[inline] #[inline]
pub fn vsize(&self) -> usize { pub fn vsize(&self) -> usize {
let weight = self.weight(); // No overflow because it's computed from data in memory
(weight + WITNESS_SCALE_FACTOR - 1) / WITNESS_SCALE_FACTOR self.weight().to_vbytes_ceil() as usize
} }
/// Returns the size of this transaction excluding the witness data. /// Returns the size of this transaction excluding the witness data.
@ -1259,7 +1260,7 @@ mod tests {
"a6eab3c14ab5272a58a5ba91505ba1a4b6d7a3a9fcbd187b6cd99a7b6d548cb7".to_string()); "a6eab3c14ab5272a58a5ba91505ba1a4b6d7a3a9fcbd187b6cd99a7b6d548cb7".to_string());
assert_eq!(format!("{:x}", realtx.wtxid()), assert_eq!(format!("{:x}", realtx.wtxid()),
"a6eab3c14ab5272a58a5ba91505ba1a4b6d7a3a9fcbd187b6cd99a7b6d548cb7".to_string()); "a6eab3c14ab5272a58a5ba91505ba1a4b6d7a3a9fcbd187b6cd99a7b6d548cb7".to_string());
assert_eq!(realtx.weight(), tx_bytes.len()*WITNESS_SCALE_FACTOR); assert_eq!(realtx.weight().to_wu() as usize, tx_bytes.len()*WITNESS_SCALE_FACTOR);
assert_eq!(realtx.size(), tx_bytes.len()); assert_eq!(realtx.size(), tx_bytes.len());
assert_eq!(realtx.vsize(), tx_bytes.len()); assert_eq!(realtx.vsize(), tx_bytes.len());
assert_eq!(realtx.strippedsize(), tx_bytes.len()); assert_eq!(realtx.strippedsize(), tx_bytes.len());
@ -1293,7 +1294,7 @@ mod tests {
"f5864806e3565c34d1b41e716f72609d00b55ea5eac5b924c9719a842ef42206".to_string()); "f5864806e3565c34d1b41e716f72609d00b55ea5eac5b924c9719a842ef42206".to_string());
assert_eq!(format!("{:x}", realtx.wtxid()), assert_eq!(format!("{:x}", realtx.wtxid()),
"80b7d8a82d5d5bf92905b06f2014dd699e03837ca172e3a59d51426ebbe3e7f5".to_string()); "80b7d8a82d5d5bf92905b06f2014dd699e03837ca172e3a59d51426ebbe3e7f5".to_string());
const EXPECTED_WEIGHT: usize = 442; const EXPECTED_WEIGHT: Weight = Weight::from_wu(442);
assert_eq!(realtx.weight(), EXPECTED_WEIGHT); assert_eq!(realtx.weight(), EXPECTED_WEIGHT);
assert_eq!(realtx.size(), tx_bytes.len()); assert_eq!(realtx.size(), tx_bytes.len());
assert_eq!(realtx.vsize(), 111); assert_eq!(realtx.vsize(), 111);
@ -1302,12 +1303,12 @@ mod tests {
// weight = WITNESS_SCALE_FACTOR * stripped_size + witness_size // weight = WITNESS_SCALE_FACTOR * stripped_size + witness_size
// then, // then,
// stripped_size = (weight - size) / (WITNESS_SCALE_FACTOR - 1) // stripped_size = (weight - size) / (WITNESS_SCALE_FACTOR - 1)
let expected_strippedsize = (EXPECTED_WEIGHT - tx_bytes.len()) / (WITNESS_SCALE_FACTOR - 1); let expected_strippedsize = (EXPECTED_WEIGHT.to_wu() as usize - tx_bytes.len()) / (WITNESS_SCALE_FACTOR - 1);
assert_eq!(realtx.strippedsize(), expected_strippedsize); assert_eq!(realtx.strippedsize(), expected_strippedsize);
// Construct a transaction without the witness data. // Construct a transaction without the witness data.
let mut tx_without_witness = realtx; let mut tx_without_witness = realtx;
tx_without_witness.input.iter_mut().for_each(|input| input.witness.clear()); tx_without_witness.input.iter_mut().for_each(|input| input.witness.clear());
assert_eq!(tx_without_witness.weight(), expected_strippedsize*WITNESS_SCALE_FACTOR); assert_eq!(tx_without_witness.weight().to_wu() as usize, expected_strippedsize*WITNESS_SCALE_FACTOR);
assert_eq!(tx_without_witness.size(), expected_strippedsize); assert_eq!(tx_without_witness.size(), expected_strippedsize);
assert_eq!(tx_without_witness.vsize(), expected_strippedsize); assert_eq!(tx_without_witness.vsize(), expected_strippedsize);
assert_eq!(tx_without_witness.strippedsize(), expected_strippedsize); assert_eq!(tx_without_witness.strippedsize(), expected_strippedsize);
@ -1412,7 +1413,7 @@ mod tests {
assert_eq!(format!("{:x}", tx.wtxid()), "d6ac4a5e61657c4c604dcde855a1db74ec6b3e54f32695d72c5e11c7761ea1b4"); assert_eq!(format!("{:x}", tx.wtxid()), "d6ac4a5e61657c4c604dcde855a1db74ec6b3e54f32695d72c5e11c7761ea1b4");
assert_eq!(format!("{:x}", tx.txid()), "9652aa62b0e748caeec40c4cb7bc17c6792435cc3dfe447dd1ca24f912a1c6ec"); assert_eq!(format!("{:x}", tx.txid()), "9652aa62b0e748caeec40c4cb7bc17c6792435cc3dfe447dd1ca24f912a1c6ec");
assert_eq!(tx.weight(), 2718); assert_eq!(tx.weight(), Weight::from_wu(2718));
// non-segwit tx from my mempool // non-segwit tx from my mempool
let tx_bytes = hex!( let tx_bytes = hex!(
@ -1444,7 +1445,7 @@ mod tests {
fn test_segwit_tx_decode() { fn test_segwit_tx_decode() {
let tx_bytes = hex!("010000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff3603da1b0e00045503bd5704c7dd8a0d0ced13bb5785010800000000000a636b706f6f6c122f4e696e6a61506f6f6c2f5345475749542fffffffff02b4e5a212000000001976a914876fbb82ec05caa6af7a3b5e5a983aae6c6cc6d688ac0000000000000000266a24aa21a9edf91c46b49eb8a29089980f02ee6b57e7d63d33b18b4fddac2bcd7db2a39837040120000000000000000000000000000000000000000000000000000000000000000000000000"); let tx_bytes = hex!("010000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff3603da1b0e00045503bd5704c7dd8a0d0ced13bb5785010800000000000a636b706f6f6c122f4e696e6a61506f6f6c2f5345475749542fffffffff02b4e5a212000000001976a914876fbb82ec05caa6af7a3b5e5a983aae6c6cc6d688ac0000000000000000266a24aa21a9edf91c46b49eb8a29089980f02ee6b57e7d63d33b18b4fddac2bcd7db2a39837040120000000000000000000000000000000000000000000000000000000000000000000000000");
let tx: Transaction = deserialize(&tx_bytes).unwrap(); let tx: Transaction = deserialize(&tx_bytes).unwrap();
assert_eq!(tx.weight(), 780); assert_eq!(tx.weight(), Weight::from_wu(780));
serde_round_trip!(tx); serde_round_trip!(tx);
let consensus_encoded = serialize(&tx); let consensus_encoded = serialize(&tx);
@ -1603,7 +1604,7 @@ mod tests {
(false, "0100000001c336895d9fa674f8b1e294fd006b1ac8266939161600e04788c515089991b50a030000006a47304402204213769e823984b31dcb7104f2c99279e74249eacd4246dabcf2575f85b365aa02200c3ee89c84344ae326b637101a92448664a8d39a009c8ad5d147c752cbe112970121028b1b44b4903c9103c07d5a23e3c7cf7aeb0ba45ddbd2cfdce469ab197381f195fdffffff040000000000000000536a4c5058325bb7b7251cf9e36cac35d691bd37431eeea426d42cbdecca4db20794f9a4030e6cb5211fabf887642bcad98c9994430facb712da8ae5e12c9ae5ff314127d33665000bb26c0067000bb0bf00322a50c300000000000017a9145ca04fdc0a6d2f4e3f67cfeb97e438bb6287725f8750c30000000000001976a91423086a767de0143523e818d4273ddfe6d9e4bbcc88acc8465003000000001976a914c95cbacc416f757c65c942f9b6b8a20038b9b12988ac00000000"), (false, "0100000001c336895d9fa674f8b1e294fd006b1ac8266939161600e04788c515089991b50a030000006a47304402204213769e823984b31dcb7104f2c99279e74249eacd4246dabcf2575f85b365aa02200c3ee89c84344ae326b637101a92448664a8d39a009c8ad5d147c752cbe112970121028b1b44b4903c9103c07d5a23e3c7cf7aeb0ba45ddbd2cfdce469ab197381f195fdffffff040000000000000000536a4c5058325bb7b7251cf9e36cac35d691bd37431eeea426d42cbdecca4db20794f9a4030e6cb5211fabf887642bcad98c9994430facb712da8ae5e12c9ae5ff314127d33665000bb26c0067000bb0bf00322a50c300000000000017a9145ca04fdc0a6d2f4e3f67cfeb97e438bb6287725f8750c30000000000001976a91423086a767de0143523e818d4273ddfe6d9e4bbcc88acc8465003000000001976a914c95cbacc416f757c65c942f9b6b8a20038b9b12988ac00000000"),
]; ];
let empty_transaction_size = Transaction { let empty_transaction_weight = Transaction {
version: 0, version: 0,
lock_time: absolute::LockTime::ZERO, lock_time: absolute::LockTime::ZERO,
input: vec![], input: vec![],
@ -1620,12 +1621,12 @@ mod tests {
let tx: Transaction = deserialize(Vec::from_hex(tx).unwrap().as_slice()).unwrap(); let tx: Transaction = deserialize(Vec::from_hex(tx).unwrap().as_slice()).unwrap();
// The empty tx size doesn't include the segwit marker (`0001`), so, in case of segwit txs, // The empty tx size doesn't include the segwit marker (`0001`), so, in case of segwit txs,
// we have to manually add it ourselves // we have to manually add it ourselves
let segwit_marker_size = if *is_segwit { 2 } else { 0 }; let segwit_marker_weight = if *is_segwit { 2 } else { 0 };
let calculated_size = empty_transaction_size let calculated_size = empty_transaction_weight.to_wu() as usize
+ segwit_marker_size + segwit_marker_weight
+ tx.input.iter().fold(0, |sum, i| sum + txin_weight(i)) + tx.input.iter().fold(0, |sum, i| sum + txin_weight(i))
+ tx.output.iter().fold(0, |sum, o| sum + o.weight()); + tx.output.iter().fold(0, |sum, o| sum + o.weight());
assert_eq!(calculated_size, tx.weight()); assert_eq!(calculated_size, tx.weight().to_wu() as usize);
} }
} }
} }

View File

@ -0,0 +1,198 @@
//! Implements `Weight` and associated features.
use core::fmt;
use core::ops::{Add, AddAssign, Sub, SubAssign, Mul, MulAssign, Div, DivAssign};
/// Represents block weight - the weight of a transaction or block.
///
/// This is an integer newtype representing weigth in `wu`. It provides protection against mixing
/// up the types as well as basic formatting features.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(crate = "actual_serde"))]
#[cfg_attr(feature = "serde", serde(transparent))]
pub struct Weight(u64);
impl Weight {
/// 0 wu.
///
/// Equivalent to [`MIN`](Self::MIN), may better express intent in some contexts.
pub const ZERO: Weight = Weight(0);
/// Minimum possible value (0 wu).
///
/// Equivalent to [`ZERO`](Self::ZERO), may better express intent in some contexts.
pub const MIN: Weight = Weight(u64::min_value());
/// Maximum possible value.
pub const MAX: Weight = Weight(u64::max_value());
/// Directly constructs `Weight` from weight units.
pub const fn from_wu(wu: u64) -> Self {
Weight(wu)
}
/// Constructs `Weight` from virtual bytes.
///
/// # Errors
///
/// Returns `None` on overflow.
pub fn from_vb(vb: u64) -> Option<Self> {
vb.checked_mul(4).map(Weight::from_wu)
}
/// Constructs `Weight` from virtual bytes without overflow check.
pub const fn from_vb_unchecked(vb: u64) -> Self {
Weight::from_wu(vb * 4)
}
/// Constructs `Weight` from witness size.
pub const fn from_witness_data_size(witness_size: u64) -> Self {
Weight(witness_size)
}
/// Constructs `Weight` from non-witness size.
pub const fn from_non_witness_data_size(non_witness_size: u64) -> Self {
Weight(non_witness_size * 4)
}
/// Returns raw weight units.
///
/// Can be used instead of `into()` to avoid inference issues.
pub const fn to_wu(self) -> u64 {
self.0
}
/// Converts to vB rounding down.
pub const fn to_vbytes_floor(self) -> u64 {
self.0 / 4
}
/// Converts to vB rounding up.
pub const fn to_vbytes_ceil(self) -> u64 {
(self.0 + 3) / 4
}
/// Checked addition.
///
/// Computes `self + rhs` returning `None` if overflow occurred.
pub fn checked_add(self, rhs: Self) -> Option<Self> {
self.0.checked_add(rhs.0).map(Self)
}
/// Checked subtraction.
///
/// Computes `self - rhs` returning `None` if overflow occurred.
pub fn checked_sub(self, rhs: Self) -> Option<Self> {
self.0.checked_add(rhs.0).map(Self)
}
/// Checked multiplication.
///
/// Computes `self * rhs` returning `None` if overflow occurred.
pub fn checked_mul(self, rhs: u64) -> Option<Self> {
self.0.checked_mul(rhs).map(Self)
}
/// Checked division.
///
/// Computes `self / rhs` returning `None` if `rhs == 0`.
pub fn checked_div(self, rhs: u64) -> Option<Self> {
self.0.checked_div(rhs).map(Self)
}
}
/// Alternative will display the unit.
impl fmt::Display for Weight {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if f.alternate() {
write!(f, "{} wu", self.0)
} else {
fmt::Display::fmt(&self.0, f)
}
}
}
impl From<Weight> for u64 {
fn from(value: Weight) -> Self {
value.to_wu()
}
}
impl Add for Weight {
type Output = Weight;
fn add(self, rhs: Weight) -> Self::Output {
Weight(self.0 + rhs.0)
}
}
impl AddAssign for Weight {
fn add_assign(&mut self, rhs: Self) {
self.0 += rhs.0
}
}
impl Sub for Weight {
type Output = Weight;
fn sub(self, rhs: Weight) -> Self::Output {
Weight(self.0 - rhs.0)
}
}
impl SubAssign for Weight {
fn sub_assign(&mut self, rhs: Self) {
self.0 -= rhs.0
}
}
impl Mul<u64> for Weight {
type Output = Weight;
fn mul(self, rhs: u64) -> Self::Output {
Weight(self.0 * rhs)
}
}
impl Mul<Weight> for u64 {
type Output = Weight;
fn mul(self, rhs: Weight) -> Self::Output {
Weight(self * rhs.0)
}
}
impl MulAssign<u64> for Weight {
fn mul_assign(&mut self, rhs: u64) {
self.0 *= rhs
}
}
impl Div<u64> for Weight {
type Output = Weight;
fn div(self, rhs: u64) -> Self::Output {
Weight(self.0 / rhs)
}
}
impl DivAssign<u64> for Weight {
fn div_assign(&mut self, rhs: u64) {
self.0 /= rhs
}
}
impl core::iter::Sum for Weight {
fn sum<I>(iter: I) -> Self where I: Iterator<Item = Self> {
Weight(iter.map(Weight::to_wu).sum())
}
}
impl<'a> core::iter::Sum<&'a Weight> for Weight {
fn sum<I>(iter: I) -> Self where I: Iterator<Item = &'a Weight> {
iter.cloned().sum()
}
}
crate::parse::impl_parse_str_through_int!(Weight);

View File

@ -116,7 +116,7 @@ pub(crate) use impl_tryfrom_str_through_int_single;
/// The `Error` type is `ParseIntError` /// The `Error` type is `ParseIntError`
macro_rules! impl_parse_str_through_int { macro_rules! impl_parse_str_through_int {
($to:ident $(, $fn:ident)?) => { ($to:ident $(, $fn:ident)?) => {
$crate::parse::impl_tryfrom_str_through_int_single!(&str, $to $(, $fn)?; String, $to $(, $fn)?; Box<str>, $to $(, $fn)?); $crate::parse::impl_tryfrom_str_through_int_single!(&str, $to $(, $fn)?; alloc::string::String, $to $(, $fn)?; alloc::boxed::Box<str>, $to $(, $fn)?);
impl core::str::FromStr for $to { impl core::str::FromStr for $to {
type Err = $crate::error::ParseIntError; type Err = $crate::error::ParseIntError;