dpp/fee/fee_result/
mod.rs1use crate::consensus::fee::balance_is_not_enough_error::BalanceIsNotEnoughError;
39use crate::consensus::fee::fee_error::FeeError;
40
41use crate::fee::fee_result::refunds::FeeRefunds;
42use crate::fee::fee_result::BalanceChange::{AddToBalance, NoBalanceChange, RemoveFromBalance};
43use crate::fee::Credits;
44use crate::prelude::UserFeeIncrease;
45use crate::ProtocolError;
46use platform_value::Identifier;
47use std::cmp::Ordering;
48use std::collections::BTreeMap;
49use std::convert::TryFrom;
50
51pub mod refunds;
52
53#[derive(Debug, Clone, Eq, PartialEq, Default)]
55pub struct FeeResult {
56 pub storage_fee: Credits,
58 pub processing_fee: Credits,
60 pub fee_refunds: FeeRefunds,
62 pub removed_bytes_from_system: u32,
64}
65
66impl TryFrom<Vec<FeeResult>> for FeeResult {
67 type Error = ProtocolError;
68 fn try_from(value: Vec<FeeResult>) -> Result<Self, Self::Error> {
69 let mut aggregate_fee_result = FeeResult::default();
70 value
71 .into_iter()
72 .try_for_each(|fee_result| aggregate_fee_result.checked_add_assign(fee_result))?;
73 Ok(aggregate_fee_result)
74 }
75}
76
77impl TryFrom<Vec<Option<FeeResult>>> for FeeResult {
78 type Error = ProtocolError;
79 fn try_from(value: Vec<Option<FeeResult>>) -> Result<Self, Self::Error> {
80 let mut aggregate_fee_result = FeeResult::default();
81 value.into_iter().try_for_each(|fee_result| {
82 if let Some(fee_result) = fee_result {
83 aggregate_fee_result.checked_add_assign(fee_result)
84 } else {
85 Ok(())
86 }
87 })?;
88 Ok(aggregate_fee_result)
89 }
90}
91
92#[derive(Clone, Debug, PartialEq, Eq)]
94pub enum BalanceChange {
95 AddToBalance(Credits),
97 RemoveFromBalance {
99 required_removed_balance: Credits,
101 desired_removed_balance: Credits,
103 },
104 NoBalanceChange,
106}
107
108#[derive(Clone, Debug)]
110pub struct BalanceChangeForIdentity {
111 pub identity_id: Identifier,
113
114 fee_result: FeeResult,
115 change: BalanceChange,
116}
117
118impl BalanceChangeForIdentity {
119 pub fn change(&self) -> &BalanceChange {
121 &self.change
122 }
123
124 pub fn other_refunds(&self) -> BTreeMap<Identifier, Credits> {
126 self.fee_result
127 .fee_refunds
128 .calculate_all_refunds_except_identity(self.identity_id)
129 }
130
131 pub fn into_fee_result(self) -> FeeResult {
133 self.fee_result
134 }
135
136 fn into_fee_result_less_processing_debt(self, processing_debt: u64) -> FeeResult {
138 FeeResult {
139 processing_fee: self.fee_result.processing_fee - processing_debt,
140 ..self.fee_result
141 }
142 }
143
144 pub fn fee_result_outcome<E>(self, user_balance: u64) -> Result<FeeResult, E>
146 where
147 E: From<FeeError>,
148 {
149 match self.change {
150 AddToBalance { .. } => {
151 Ok(self.into_fee_result())
154 }
155 RemoveFromBalance {
156 required_removed_balance,
157 desired_removed_balance,
158 } => {
159 if user_balance >= desired_removed_balance {
160 Ok(self.into_fee_result())
161 } else if user_balance >= required_removed_balance {
162 Ok(self.into_fee_result_less_processing_debt(
165 desired_removed_balance - user_balance,
166 ))
167 } else {
168 Err(
170 FeeError::BalanceIsNotEnoughError(BalanceIsNotEnoughError::new(
171 user_balance,
172 required_removed_balance,
173 ))
174 .into(),
175 )
176 }
177 }
178 NoBalanceChange => {
179 Ok(self.into_fee_result())
181 }
182 }
183 }
184}
185
186impl FeeResult {
187 pub fn new_from_processing_fee(credits: Credits) -> Self {
189 Self {
190 storage_fee: 0,
191 processing_fee: credits,
192 fee_refunds: Default::default(),
193 removed_bytes_from_system: 0,
194 }
195 }
196
197 pub fn apply_user_fee_increase(&mut self, add_fee_percentage_multiplier: UserFeeIncrease) {
199 let additional_processing_fee = (self.processing_fee as u128)
200 .saturating_mul(add_fee_percentage_multiplier as u128)
201 .saturating_div(100);
202 if additional_processing_fee > u64::MAX as u128 {
203 self.processing_fee = u64::MAX;
204 } else {
205 self.processing_fee = self
206 .processing_fee
207 .saturating_add(additional_processing_fee as u64);
208 }
209 }
210
211 pub fn total_base_fee(&self) -> Credits {
213 self.storage_fee.saturating_add(self.processing_fee)
214 }
215
216 pub fn into_balance_change(self, identity_id: Identifier) -> BalanceChangeForIdentity {
218 let storage_credits_returned = self
219 .fee_refunds
220 .calculate_refunds_amount_for_identity(identity_id)
221 .unwrap_or_default();
222
223 let base_required_removed_balance = self.storage_fee;
224 let base_desired_removed_balance = self.storage_fee + self.processing_fee;
225
226 let balance_change = match storage_credits_returned.cmp(&base_desired_removed_balance) {
227 Ordering::Less => {
228 let required_removed_balance =
230 base_required_removed_balance.saturating_sub(storage_credits_returned);
231
232 let desired_removed_balance =
233 base_desired_removed_balance - storage_credits_returned;
234
235 RemoveFromBalance {
236 required_removed_balance,
237 desired_removed_balance,
238 }
239 }
240 Ordering::Equal => NoBalanceChange,
241 Ordering::Greater => {
242 AddToBalance(storage_credits_returned - base_desired_removed_balance)
244 }
245 };
246
247 BalanceChangeForIdentity {
248 identity_id,
249 fee_result: self,
250 change: balance_change,
251 }
252 }
253
254 pub fn default_with_fees(storage_fee: Credits, processing_fee: Credits) -> Self {
256 FeeResult {
257 storage_fee,
258 processing_fee,
259 ..Default::default()
260 }
261 }
262
263 pub fn checked_add_assign(&mut self, rhs: Self) -> Result<(), ProtocolError> {
265 self.storage_fee = self
266 .storage_fee
267 .checked_add(rhs.storage_fee)
268 .ok_or(ProtocolError::Overflow("storage fee overflow error"))?;
269 self.processing_fee = self
270 .processing_fee
271 .checked_add(rhs.processing_fee)
272 .ok_or(ProtocolError::Overflow("processing fee overflow error"))?;
273 self.fee_refunds.checked_add_assign(rhs.fee_refunds)?;
274 self.removed_bytes_from_system = self
275 .removed_bytes_from_system
276 .checked_add(rhs.removed_bytes_from_system)
277 .ok_or(ProtocolError::Overflow(
278 "removed_bytes_from_system overflow error",
279 ))?;
280 Ok(())
281 }
282}