bc/
weights.rs

1// Bitcoin protocol consensus library.
2//
3// SPDX-License-Identifier: Apache-2.0
4//
5// Written in 2019-2024 by
6//     Dr Maxim Orlovsky <orlovsky@lnp-bp.org>
7//
8// Copyright (C) 2019-2024 LNP/BP Standards Association. All rights reserved.
9//
10// Licensed under the Apache License, Version 2.0 (the "License");
11// you may not use this file except in compliance with the License.
12// You may obtain a copy of the License at
13//
14//     http://www.apache.org/licenses/LICENSE-2.0
15//
16// Unless required by applicable law or agreed to in writing, software
17// distributed under the License is distributed on an "AS IS" BASIS,
18// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19// See the License for the specific language governing permissions and
20// limitations under the License.
21
22use std::iter::Sum;
23use std::ops::{Add, AddAssign};
24
25use crate::{LenVarInt, ScriptPubkey, SigScript, Tx, TxIn, TxOut, Witness, LIB_NAME_BITCOIN};
26
27#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, Display)]
28#[derive(StrictType, StrictEncode, StrictDecode, StrictDumb)]
29#[strict_type(lib = LIB_NAME_BITCOIN)]
30#[display("{0} vbytes")]
31pub struct VBytes(u32);
32
33impl Add for VBytes {
34    type Output = Self;
35    fn add(self, rhs: Self) -> Self::Output { Self(self.0 + rhs.0) }
36}
37
38impl AddAssign for VBytes {
39    fn add_assign(&mut self, rhs: Self) { self.0.add_assign(rhs.0) }
40}
41
42impl Sum for VBytes {
43    fn sum<I: Iterator<Item = Self>>(iter: I) -> Self { Self(iter.map(Self::into_u32).sum()) }
44}
45
46impl VBytes {
47    pub fn to_u32(&self) -> u32 { self.0 }
48    pub fn into_u32(self) -> u32 { self.0 }
49}
50
51#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, Display)]
52#[derive(StrictType, StrictEncode, StrictDecode, StrictDumb)]
53#[strict_type(lib = LIB_NAME_BITCOIN)]
54#[display("{0} WU")]
55pub struct WeightUnits(u32);
56
57impl Add for WeightUnits {
58    type Output = Self;
59    fn add(self, rhs: Self) -> Self::Output { Self(self.0 + rhs.0) }
60}
61
62impl AddAssign for WeightUnits {
63    fn add_assign(&mut self, rhs: Self) { self.0.add_assign(rhs.0) }
64}
65
66impl Sum for WeightUnits {
67    fn sum<I: Iterator<Item = Self>>(iter: I) -> Self { Self(iter.map(Self::into_u32).sum()) }
68}
69
70impl From<WeightUnits> for VBytes {
71    fn from(wu: WeightUnits) -> Self { Self((wu.0 as f32 / 4.0).ceil() as u32) }
72}
73
74impl WeightUnits {
75    pub fn no_discount(bytes: usize) -> Self { WeightUnits(bytes as u32 * 4) }
76    pub fn witness_discount(bytes: usize) -> Self { WeightUnits(bytes as u32) }
77    pub fn to_u32(&self) -> u32 { self.0 }
78    pub fn into_u32(self) -> u32 { self.0 }
79}
80
81pub trait Weight {
82    fn weight_units(&self) -> WeightUnits;
83
84    #[inline]
85    fn vbytes(&self) -> VBytes { VBytes::from(self.weight_units()) }
86}
87
88impl Weight for Tx {
89    fn weight_units(&self) -> WeightUnits {
90        let bytes = 4 // version
91        + self.inputs.len_var_int().len()
92        + self.outputs.len_var_int().len()
93        + 4; // lock time
94
95        let mut weight = WeightUnits::no_discount(bytes)
96            + self.inputs().map(TxIn::weight_units).sum()
97            + self.outputs().map(TxOut::weight_units).sum();
98        if self.is_segwit() {
99            weight += WeightUnits::witness_discount(2); // marker and flag bytes
100            weight += self.inputs().map(|txin| &txin.witness).map(Witness::weight_units).sum();
101        }
102        weight
103    }
104}
105
106impl Weight for TxIn {
107    fn weight_units(&self) -> WeightUnits {
108        WeightUnits::no_discount(
109            32 // txid
110            + 4 // vout
111            + 4, // nseq
112        ) + self.sig_script.weight_units()
113    }
114}
115
116impl Weight for TxOut {
117    fn weight_units(&self) -> WeightUnits {
118        WeightUnits::no_discount(8) // value
119        + self.script_pubkey.weight_units()
120    }
121}
122
123impl Weight for ScriptPubkey {
124    fn weight_units(&self) -> WeightUnits {
125        WeightUnits::no_discount(self.len_var_int().len() + self.len())
126    }
127}
128
129impl Weight for SigScript {
130    fn weight_units(&self) -> WeightUnits {
131        WeightUnits::no_discount(self.len_var_int().len() + self.len())
132    }
133}
134
135impl Weight for Witness {
136    fn weight_units(&self) -> WeightUnits {
137        WeightUnits::witness_discount(
138            self.len_var_int().len()
139                + self.iter().map(|item| item.len_var_int().len() + item.len()).sum::<usize>(),
140        )
141    }
142}