bee_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::{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 key_factor(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 data_factor(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(
76    feature = "serde",
77    derive(serde::Serialize, serde::Deserialize),
78    serde(rename_all = "camelCase")
79)]
80pub struct RentStructure {
81    /// Cost in tokens per virtual byte.
82    pub v_byte_cost: u32,
83    /// The weight factor used for key fields in the outputs.
84    pub v_byte_factor_key: u8,
85    /// The weight factor used for data fields in the outputs.
86    pub v_byte_factor_data: u8,
87    /// The offset in addition to the other fields.
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    /// Returns a builder for this config.
99    pub fn build() -> RentStructureBuilder {
100        RentStructureBuilder::new()
101    }
102}
103
104impl Packable for RentStructure {
105    type UnpackError = Error;
106    type UnpackVisitor = ();
107
108    fn pack<P: Packer>(&self, packer: &mut P) -> Result<(), P::Error> {
109        self.v_byte_cost.pack(packer)?;
110        self.v_byte_factor_key.pack(packer)?;
111        self.v_byte_factor_data.pack(packer)?;
112
113        Ok(())
114    }
115
116    fn unpack<U: Unpacker, const VERIFY: bool>(
117        unpacker: &mut U,
118        visitor: &Self::UnpackVisitor,
119    ) -> Result<Self, UnpackError<Self::UnpackError, U::Error>> {
120        let v_byte_cost = u32::unpack::<_, VERIFY>(unpacker, visitor).coerce()?;
121        let v_byte_factor_key = u8::unpack::<_, VERIFY>(unpacker, visitor).coerce()?;
122        let v_byte_factor_data = u8::unpack::<_, VERIFY>(unpacker, visitor).coerce()?;
123        let v_byte_offset = v_byte_offset(v_byte_factor_key, v_byte_factor_data);
124
125        Ok(Self {
126            v_byte_cost,
127            v_byte_factor_key,
128            v_byte_factor_data,
129            v_byte_offset,
130        })
131    }
132}
133
134/// A trait to facilitate the computation of the byte cost of block outputs, which is central to dust protection.
135pub trait Rent {
136    /// Different fields in a type lead to different storage requirements for the ledger state.
137    fn weighted_bytes(&self, config: &RentStructure) -> u64;
138
139    /// Computes the rent cost given a [`RentStructure`].
140    fn rent_cost(&self, config: &RentStructure) -> u64 {
141        config.v_byte_cost as u64 * (self.weighted_bytes(config) + config.v_byte_offset as u64)
142    }
143}
144
145impl<T: Rent, const N: usize> Rent for [T; N] {
146    fn weighted_bytes(&self, config: &RentStructure) -> u64 {
147        self.iter().map(|elem| elem.weighted_bytes(config)).sum()
148    }
149}
150
151fn v_byte_offset(v_byte_factor_key: u8, v_byte_factor_data: u8) -> u32 {
152    size_of::<OutputId>() as u32 * v_byte_factor_key as u32
153        + size_of::<BlockId>() as u32 * v_byte_factor_data as u32
154        + size_of::<MilestoneIndex>() as u32 * v_byte_factor_data as u32
155        + size_of::<ConfirmationUnixTimestamp>() as u32 * v_byte_factor_data as u32
156}