Skip to main content

iota_types/block/output/
rent.rs

1// Copyright 2022 IOTA Stiftung
2// SPDX-License-Identifier: Apache-2.0
3
4use core::mem::size_of;
5
6use packable::{
7    error::{UnpackError, UnpackErrorExt},
8    packer::Packer,
9    unpacker::Unpacker,
10    Packable,
11};
12
13use crate::block::{output::OutputId, payload::milestone::MilestoneIndex, BlockId, Error};
14
15const DEFAULT_BYTE_COST: u32 = 100;
16const DEFAULT_BYTE_COST_FACTOR_KEY: u8 = 10;
17const DEFAULT_BYTE_COST_FACTOR_DATA: u8 = 1;
18
19type ConfirmationUnixTimestamp = u32;
20
21/// Builder for a [`RentStructure`].
22#[derive(Default, Eq, PartialEq)]
23#[cfg_attr(feature = "serde", derive(serde::Deserialize))]
24#[must_use]
25pub struct RentStructureBuilder {
26    #[cfg_attr(feature = "serde", serde(alias = "vByteCost"))]
27    v_byte_cost: Option<u32>,
28    #[cfg_attr(feature = "serde", serde(alias = "vByteFactorKey"))]
29    v_byte_factor_key: Option<u8>,
30    #[cfg_attr(feature = "serde", serde(alias = "vByteFactorData"))]
31    v_byte_factor_data: Option<u8>,
32}
33
34impl RentStructureBuilder {
35    /// Returns a new [`RentStructureBuilder`].
36    pub fn new() -> Self {
37        Default::default()
38    }
39
40    /// Sets the byte cost for the storage deposit.
41    pub fn byte_cost(mut self, byte_cost: u32) -> Self {
42        self.v_byte_cost.replace(byte_cost);
43        self
44    }
45
46    /// Sets the virtual byte weight for the key fields.
47    pub fn byte_factor_key(mut self, weight: u8) -> Self {
48        self.v_byte_factor_key.replace(weight);
49        self
50    }
51
52    /// Sets the virtual byte weight for the data fields.
53    pub fn byte_factor_data(mut self, weight: u8) -> Self {
54        self.v_byte_factor_data.replace(weight);
55        self
56    }
57
58    /// Returns the built [`RentStructure`].
59    pub fn finish(self) -> RentStructure {
60        let v_byte_factor_key = self.v_byte_factor_key.unwrap_or(DEFAULT_BYTE_COST_FACTOR_KEY);
61        let v_byte_factor_data = self.v_byte_factor_data.unwrap_or(DEFAULT_BYTE_COST_FACTOR_DATA);
62        let v_byte_offset = v_byte_offset(v_byte_factor_key, v_byte_factor_data);
63
64        RentStructure {
65            v_byte_cost: self.v_byte_cost.unwrap_or(DEFAULT_BYTE_COST),
66            v_byte_factor_key,
67            v_byte_factor_data,
68            v_byte_offset,
69        }
70    }
71}
72
73/// Specifies the current parameters for the byte cost computation.
74#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
75#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
76pub struct RentStructure {
77    /// Cost in tokens per virtual byte.
78    #[cfg_attr(feature = "serde", serde(alias = "vByteCost"))]
79    v_byte_cost: u32,
80    /// The weight factor used for key fields in the outputs.
81    #[cfg_attr(feature = "serde", serde(alias = "vByteFactorKey"))]
82    v_byte_factor_key: u8,
83    /// The weight factor used for data fields in the outputs.
84    #[cfg_attr(feature = "serde", serde(alias = "vByteFactorData"))]
85    v_byte_factor_data: u8,
86    /// The offset in addition to the other fields.
87    #[cfg_attr(feature = "serde", serde(alias = "vByteOffset"))]
88    v_byte_offset: u32,
89}
90
91impl Default for RentStructure {
92    fn default() -> Self {
93        RentStructureBuilder::new().finish()
94    }
95}
96
97impl RentStructure {
98    /// Creates a new [`RentStructure`].
99    pub fn new(byte_cost: u32, byte_factor_key: u8, byte_factor_data: u8) -> Self {
100        Self::build()
101            .byte_cost(byte_cost)
102            .byte_factor_key(byte_factor_key)
103            .byte_factor_data(byte_factor_data)
104            .finish()
105    }
106
107    /// Returns a builder for a [`RentStructure`].
108    pub fn build() -> RentStructureBuilder {
109        RentStructureBuilder::new()
110    }
111
112    /// Returns the byte cost of the [`RentStructure`].
113    pub fn byte_cost(&self) -> u32 {
114        self.v_byte_cost
115    }
116
117    /// Returns the byte factor key of the [`RentStructure`].
118    pub fn byte_factor_key(&self) -> u8 {
119        self.v_byte_factor_key
120    }
121
122    /// Returns the byte factor data of the [`RentStructure`].
123    pub fn byte_factor_data(&self) -> u8 {
124        self.v_byte_factor_data
125    }
126
127    /// Returns the byte offset of the [`RentStructure`].
128    pub fn byte_offset(&self) -> u32 {
129        self.v_byte_offset
130    }
131}
132
133impl Packable for RentStructure {
134    type UnpackError = Error;
135    type UnpackVisitor = ();
136
137    fn pack<P: Packer>(&self, packer: &mut P) -> Result<(), P::Error> {
138        self.v_byte_cost.pack(packer)?;
139        self.v_byte_factor_data.pack(packer)?;
140        self.v_byte_factor_key.pack(packer)?;
141
142        Ok(())
143    }
144
145    fn unpack<U: Unpacker, const VERIFY: bool>(
146        unpacker: &mut U,
147        visitor: &Self::UnpackVisitor,
148    ) -> Result<Self, UnpackError<Self::UnpackError, U::Error>> {
149        let v_byte_cost = u32::unpack::<_, VERIFY>(unpacker, visitor).coerce()?;
150        let v_byte_factor_data = u8::unpack::<_, VERIFY>(unpacker, visitor).coerce()?;
151        let v_byte_factor_key = u8::unpack::<_, VERIFY>(unpacker, visitor).coerce()?;
152        let v_byte_offset = v_byte_offset(v_byte_factor_key, v_byte_factor_data);
153
154        Ok(Self {
155            v_byte_cost,
156            v_byte_factor_key,
157            v_byte_factor_data,
158            v_byte_offset,
159        })
160    }
161}
162
163/// A trait to facilitate the computation of the byte cost of block outputs, which is central to dust protection.
164pub trait Rent {
165    /// Different fields in a type lead to different storage requirements for the ledger state.
166    fn weighted_bytes(&self, config: &RentStructure) -> u64;
167
168    /// Computes the rent cost given a [`RentStructure`].
169    fn rent_cost(&self, config: &RentStructure) -> u64 {
170        config.v_byte_cost as u64 * (self.weighted_bytes(config) + config.v_byte_offset as u64)
171    }
172}
173
174impl<T: Rent, const N: usize> Rent for [T; N] {
175    fn weighted_bytes(&self, config: &RentStructure) -> u64 {
176        self.iter().map(|elem| elem.weighted_bytes(config)).sum()
177    }
178}
179
180fn v_byte_offset(v_byte_factor_key: u8, v_byte_factor_data: u8) -> u32 {
181    size_of::<OutputId>() as u32 * v_byte_factor_key as u32
182        + size_of::<BlockId>() as u32 * v_byte_factor_data as u32
183        + size_of::<MilestoneIndex>() as u32 * v_byte_factor_data as u32
184        + size_of::<ConfirmationUnixTimestamp>() as u32 * v_byte_factor_data as u32
185}
186
187#[cfg(feature = "dto")]
188#[allow(missing_docs)]
189pub mod dto {
190
191    use super::*;
192
193    #[derive(Clone, Debug, PartialEq, Eq)]
194    #[cfg_attr(
195        feature = "serde",
196        derive(serde::Serialize, serde::Deserialize),
197        serde(rename_all = "camelCase")
198    )]
199    pub struct RentStructureDto {
200        pub v_byte_cost: u32,
201        pub v_byte_factor_key: u8,
202        pub v_byte_factor_data: u8,
203    }
204
205    impl From<RentStructureDto> for RentStructure {
206        fn from(value: RentStructureDto) -> Self {
207            Self::new(value.v_byte_cost, value.v_byte_factor_key, value.v_byte_factor_data)
208        }
209    }
210}