use std::iter::Sum;
use std::ops::{Add, AddAssign};
use crate::{LenVarInt, ScriptPubkey, SigScript, Tx, TxIn, TxOut, Witness, LIB_NAME_BITCOIN};
#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, Display)]
#[derive(StrictType, StrictEncode, StrictDecode, StrictDumb)]
#[strict_type(lib = LIB_NAME_BITCOIN)]
#[display("{0} vbytes")]
pub struct VBytes(u32);
impl Add for VBytes {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output { Self(self.0 + rhs.0) }
}
impl AddAssign for VBytes {
fn add_assign(&mut self, rhs: Self) { self.0.add_assign(rhs.0) }
}
impl Sum for VBytes {
fn sum<I: Iterator<Item = Self>>(iter: I) -> Self { Self(iter.map(Self::into_u32).sum()) }
}
impl VBytes {
pub fn to_u32(&self) -> u32 { self.0 }
pub fn into_u32(self) -> u32 { self.0 }
}
#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, Display)]
#[derive(StrictType, StrictEncode, StrictDecode, StrictDumb)]
#[strict_type(lib = LIB_NAME_BITCOIN)]
#[display("{0} WU")]
pub struct WeightUnits(u32);
impl Add for WeightUnits {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output { Self(self.0 + rhs.0) }
}
impl AddAssign for WeightUnits {
fn add_assign(&mut self, rhs: Self) { self.0.add_assign(rhs.0) }
}
impl Sum for WeightUnits {
fn sum<I: Iterator<Item = Self>>(iter: I) -> Self { Self(iter.map(Self::into_u32).sum()) }
}
impl From<WeightUnits> for VBytes {
fn from(wu: WeightUnits) -> Self { Self((wu.0 as f32 / 4.0).ceil() as u32) }
}
impl WeightUnits {
pub fn no_discount(bytes: usize) -> Self { WeightUnits(bytes as u32 * 4) }
pub fn witness_discount(bytes: usize) -> Self { WeightUnits(bytes as u32) }
pub fn to_u32(&self) -> u32 { self.0 }
pub fn into_u32(self) -> u32 { self.0 }
}
pub trait Weight {
fn weight_units(&self) -> WeightUnits;
#[inline]
fn vbytes(&self) -> VBytes { VBytes::from(self.weight_units()) }
}
impl Weight for Tx {
fn weight_units(&self) -> WeightUnits {
let bytes = 4 + self.inputs.len_var_int().len()
+ self.outputs.len_var_int().len()
+ 4; let mut weight = WeightUnits::no_discount(bytes)
+ self.inputs().map(TxIn::weight_units).sum()
+ self.outputs().map(TxOut::weight_units).sum();
if self.is_segwit() {
weight += WeightUnits::witness_discount(2); weight += self.inputs().map(|txin| &txin.witness).map(Witness::weight_units).sum();
}
weight
}
}
impl Weight for TxIn {
fn weight_units(&self) -> WeightUnits {
WeightUnits::no_discount(
32 + 4 + 4, ) + self.sig_script.weight_units()
}
}
impl Weight for TxOut {
fn weight_units(&self) -> WeightUnits {
WeightUnits::no_discount(8) + self.script_pubkey.weight_units()
}
}
impl Weight for ScriptPubkey {
fn weight_units(&self) -> WeightUnits {
WeightUnits::no_discount(self.len_var_int().len() + self.len())
}
}
impl Weight for SigScript {
fn weight_units(&self) -> WeightUnits {
WeightUnits::no_discount(self.len_var_int().len() + self.len())
}
}
impl Weight for Witness {
fn weight_units(&self) -> WeightUnits {
WeightUnits::witness_discount(
self.len_var_int().len()
+ self.iter().map(|item| item.len_var_int().len() + item.len()).sum::<usize>(),
)
}
}