1use crate::fee::epoch::{EpochIndex, SignedCreditsPerEpoch, PERPETUAL_STORAGE_ERAS};
40use rust_decimal::prelude::*;
41use rust_decimal::Decimal;
42use rust_decimal_macros::dec;
43use std::cmp::Ordering;
44
45use crate::balances::credits::Credits;
46use crate::ProtocolError;
47use std::ops::Mul;
48
49#[rustfmt::skip]
53pub const FEE_DISTRIBUTION_TABLE: [Decimal; PERPETUAL_STORAGE_ERAS as usize] = [
54 dec!(0.05000), dec!(0.04800), dec!(0.04600), dec!(0.04400), dec!(0.04200),
55 dec!(0.04000), dec!(0.03850), dec!(0.03700), dec!(0.03550), dec!(0.03400),
56 dec!(0.03250), dec!(0.03100), dec!(0.02950), dec!(0.02850), dec!(0.02750),
57 dec!(0.02650), dec!(0.02550), dec!(0.02450), dec!(0.02350), dec!(0.02250),
58 dec!(0.02150), dec!(0.02050), dec!(0.01950), dec!(0.01875), dec!(0.01800),
59 dec!(0.01725), dec!(0.01650), dec!(0.01575), dec!(0.01500), dec!(0.01425),
60 dec!(0.01350), dec!(0.01275), dec!(0.01200), dec!(0.01125), dec!(0.01050),
61 dec!(0.00975), dec!(0.00900), dec!(0.00825), dec!(0.00750), dec!(0.00675),
62 dec!(0.00600), dec!(0.00525), dec!(0.00475), dec!(0.00425), dec!(0.00375),
63 dec!(0.00325), dec!(0.00275), dec!(0.00225), dec!(0.00175), dec!(0.00125),
64];
65
66type DistributionAmount = Credits;
67type DistributionLeftovers = Credits;
68
69pub fn distribute_storage_fee_to_epochs_collection(
71 credits_per_epochs: &mut SignedCreditsPerEpoch,
72 storage_fee: Credits,
73 start_epoch_index: EpochIndex,
74 epochs_per_era: u16,
75) -> Result<DistributionLeftovers, ProtocolError> {
76 distribution_storage_fee_to_epochs_map(
77 storage_fee,
78 start_epoch_index,
79 |epoch_index, epoch_fee_share| {
80 let epoch_credits = credits_per_epochs.entry(epoch_index).or_default();
81
82 *epoch_credits = epoch_credits
83 .checked_add_unsigned(epoch_fee_share)
84 .ok_or_else(|| {
85 ProtocolError::Overflow(
86 "updated epoch credits are not fitting to credits max size",
87 )
88 })?;
89
90 Ok(())
91 },
92 epochs_per_era,
93 )
94}
95
96pub fn subtract_refunds_from_epoch_credits_collection(
99 credits_per_epochs: &mut SignedCreditsPerEpoch,
100 storage_fee: Credits,
101 start_epoch_index: EpochIndex,
102 current_epoch_index: EpochIndex,
103 epochs_per_era: u16,
104) -> Result<(), ProtocolError> {
105 let leftovers = refund_storage_fee_to_epochs_map(
106 storage_fee,
107 start_epoch_index,
108 current_epoch_index + 1,
109 |epoch_index, epoch_fee_share| {
110 let epoch_credits = credits_per_epochs.entry(epoch_index).or_default();
111
112 *epoch_credits = epoch_credits
113 .checked_sub_unsigned(epoch_fee_share)
114 .ok_or_else(|| {
115 ProtocolError::Overflow(
116 "updated epoch credits are not fitting to credits min size",
117 )
118 })?;
119
120 Ok(())
121 },
122 epochs_per_era,
123 )?;
124
125 if leftovers > 0 {
127 let epoch_credits = credits_per_epochs.entry(current_epoch_index).or_default();
128
129 *epoch_credits = epoch_credits
130 .checked_sub_unsigned(leftovers)
131 .ok_or_else(|| {
132 ProtocolError::Overflow("updated epoch credits are not fitting to credits min size")
133 })?;
134 }
135
136 Ok(())
137}
138
139pub fn calculate_storage_fee_refund_amount_and_leftovers(
141 storage_fee: Credits,
142 start_epoch_index: EpochIndex,
143 current_epoch_index: EpochIndex,
144 epochs_per_era: u16,
145) -> Result<(DistributionAmount, DistributionLeftovers), ProtocolError> {
146 let mut skipped_amount = 0;
147
148 let leftovers = distribution_storage_fee_to_epochs_map(
149 storage_fee,
150 start_epoch_index,
151 |epoch_index, epoch_fee_share| {
152 if epoch_index < current_epoch_index + 1 {
153 skipped_amount += epoch_fee_share;
154 }
155
156 Ok(())
157 },
158 epochs_per_era,
159 )?;
160
161 Ok((storage_fee - skipped_amount - leftovers, leftovers))
162}
163
164fn original_removed_credits_multiplier_from(
165 start_epoch_index: EpochIndex,
166 start_repayment_from_epoch_index: EpochIndex,
167 epochs_per_era: u16,
168) -> Result<Decimal, ProtocolError> {
169 let paid_epochs = start_repayment_from_epoch_index
175 .checked_sub(start_epoch_index)
176 .ok_or(ProtocolError::Overflow(
177 "start repayment epoch is before the original storage epoch",
178 ))?;
179
180 let current_era = (paid_epochs / epochs_per_era) as usize;
181
182 let ratio_used: Decimal =
183 FEE_DISTRIBUTION_TABLE
184 .iter()
185 .enumerate()
186 .filter_map(|(era, epoch_multiplier)| match era.cmp(¤t_era) {
187 Ordering::Less => None,
188 Ordering::Equal => {
189 let amount_epochs_left_in_era = epochs_per_era - paid_epochs % epochs_per_era;
190 Some(epoch_multiplier.mul(
191 Decimal::from(amount_epochs_left_in_era) / Decimal::from(epochs_per_era),
192 ))
193 }
194 Ordering::Greater => Some(*epoch_multiplier),
195 })
196 .sum();
197
198 if ratio_used.is_zero() {
207 return Err(ProtocolError::DivideByZero(
208 "storage fee refund is older than the entire perpetual storage window",
209 ));
210 }
211
212 Ok(dec!(1) / ratio_used)
213}
214
215fn restore_original_removed_credits_amount(
220 refund_amount: Decimal,
221 start_epoch_index: EpochIndex,
222 start_repayment_from_epoch_index: EpochIndex,
223 epochs_per_era: u16,
224) -> Result<Decimal, ProtocolError> {
225 let multiplier = original_removed_credits_multiplier_from(
226 start_epoch_index,
227 start_repayment_from_epoch_index,
228 epochs_per_era,
229 )?;
230
231 refund_amount
232 .checked_mul(multiplier)
233 .ok_or(ProtocolError::Overflow(
234 "overflow when multiplying with the multiplier (this should be impossible)",
235 ))
236}
237
238fn distribution_storage_fee_to_epochs_map<F>(
241 storage_fee: Credits,
242 start_epoch_index: EpochIndex,
243 mut map_function: F,
244 epochs_per_era: u16,
245) -> Result<DistributionLeftovers, ProtocolError>
246where
247 F: FnMut(EpochIndex, Credits) -> Result<(), ProtocolError>,
248{
249 if storage_fee == 0 {
250 return Ok(0);
251 }
252
253 let storage_fee_dec: Decimal = storage_fee.into();
254
255 let mut distribution_leftover_credits = storage_fee;
256
257 let epochs_per_era_dec = Decimal::from(epochs_per_era);
258
259 for era in 0..PERPETUAL_STORAGE_ERAS {
260 let distribution_for_that_era_ratio = FEE_DISTRIBUTION_TABLE[era as usize];
261
262 let era_fee_share = storage_fee_dec * distribution_for_that_era_ratio;
263
264 let epoch_fee_share_dec = era_fee_share / epochs_per_era_dec;
265
266 let epoch_fee_share: Credits = epoch_fee_share_dec
267 .floor()
268 .to_u64()
269 .ok_or_else(|| ProtocolError::Overflow("storage fees are not fitting in a u64"))?;
270
271 let era_start_epoch_index = start_epoch_index + epochs_per_era * era;
272
273 for epoch_index in era_start_epoch_index..era_start_epoch_index + epochs_per_era {
274 map_function(epoch_index, epoch_fee_share)?;
276
277 distribution_leftover_credits = distribution_leftover_credits
278 .checked_sub(epoch_fee_share)
279 .ok_or(ProtocolError::Overflow(
280 "leftovers bigger than initial value",
281 ))?;
282 }
283 }
284
285 Ok(distribution_leftover_credits)
286}
287
288fn refund_storage_fee_to_epochs_map<F>(
292 storage_fee: Credits,
293 start_epoch_index: EpochIndex,
294 skip_until_epoch_index: EpochIndex,
295 mut map_function: F,
296 epochs_per_era: u16,
297) -> Result<DistributionLeftovers, ProtocolError>
298where
299 F: FnMut(EpochIndex, Credits) -> Result<(), ProtocolError>,
300{
301 if storage_fee == 0 {
302 return Ok(0);
303 }
304
305 let storage_fee_dec: Decimal = storage_fee.into();
306
307 let mut distribution_leftover_credits = storage_fee;
308
309 let epochs_per_era_dec = Decimal::from(epochs_per_era);
310
311 let start_era: u16 = (skip_until_epoch_index - start_epoch_index) / epochs_per_era;
312
313 if start_era >= PERPETUAL_STORAGE_ERAS {
330 return Ok(storage_fee);
331 }
332
333 let estimated_storage_fee_dec = restore_original_removed_credits_amount(
338 storage_fee_dec,
339 start_epoch_index,
340 skip_until_epoch_index,
341 epochs_per_era,
342 )?;
343
344 for era in start_era..PERPETUAL_STORAGE_ERAS {
345 let distribution_for_that_era_ratio = FEE_DISTRIBUTION_TABLE[era as usize];
346
347 let estimated_era_fee_share = estimated_storage_fee_dec * distribution_for_that_era_ratio;
348
349 let estimated_epoch_fee_share_dec = estimated_era_fee_share / epochs_per_era_dec;
350
351 let estimated_epoch_fee_share: Credits = estimated_epoch_fee_share_dec
352 .floor()
353 .to_u64()
354 .ok_or_else(|| ProtocolError::Overflow("storage fees are not fitting in a u64"))?;
355
356 let era_start_epoch_index = if era == start_era {
357 skip_until_epoch_index
358 } else {
359 start_epoch_index + epochs_per_era * era
360 };
361
362 let era_end_epoch_index = start_epoch_index + ((era + 1) * epochs_per_era);
363
364 for epoch_index in era_start_epoch_index..era_end_epoch_index {
365 map_function(epoch_index, estimated_epoch_fee_share)?;
366
367 distribution_leftover_credits = distribution_leftover_credits
368 .checked_sub(estimated_epoch_fee_share)
369 .ok_or(ProtocolError::Overflow(
370 "leftovers bigger than initial value",
371 ))?;
372 }
373 }
374 Ok(distribution_leftover_credits)
375}
376
377#[cfg(test)]
378mod tests {
379 use super::*;
380 use crate::fee::epoch::GENESIS_EPOCH_INDEX;
381 use rust_decimal::Decimal;
382 use rust_decimal_macros::dec;
383
384 mod original_removed_credits_multiplier_from {
385 use super::*;
386
387 #[test]
388 fn should_create_multiplier_for_epochs_since_the_beginning() {
389 let epoch_0_cost = dec!(0.05000) / dec!(20.0);
391 let multiplier_should_be = dec!(1.0) / (dec!(1.0) - epoch_0_cost);
392
393 let multiplier = original_removed_credits_multiplier_from(0, 1, 20)
394 .expect("multiplier within perpetual storage window");
395
396 assert_eq!(multiplier_should_be, multiplier);
397 }
398
399 #[test]
400 fn should_create_multiplier_for_epochs_since_24_and_repaid_since_43() {
401 let epoch_0_cost = dec!(19.0) * dec!(0.05000) / dec!(20.0);
403
404 let multiplier_should_be = dec!(1.0) / (dec!(1.0) - epoch_0_cost);
405
406 let multiplier = original_removed_credits_multiplier_from(24, 43, 20)
407 .expect("multiplier within perpetual storage window");
408
409 assert_eq!(multiplier_should_be, multiplier);
410 }
411 }
412
413 mod fee_distribution_table {
414 use super::*;
415
416 #[test]
417 fn should_have_sum_of_1() {
418 assert_eq!(FEE_DISTRIBUTION_TABLE.iter().sum::<Decimal>(), dec!(1.0),);
419 }
420
421 #[test]
422 fn should_distribute_value() {
423 let value = Decimal::from(i64::MAX);
424
425 let calculated_value: Decimal = FEE_DISTRIBUTION_TABLE
426 .into_iter()
427 .map(|ratio| value * ratio)
428 .sum();
429
430 assert_eq!(calculated_value, value);
431 }
432 }
433
434 mod distribution_storage_fee_to_epochs_map {
435 use super::*;
436
437 #[test]
438 fn should_distribute_nothing_if_storage_fees_are_zero() {
439 let mut calls = 0;
440
441 let leftovers = distribution_storage_fee_to_epochs_map(
442 0,
443 GENESIS_EPOCH_INDEX,
444 |_, _| {
445 calls += 1;
446
447 Ok(())
448 },
449 20,
450 )
451 .expect("should distribute storage fee");
452
453 assert_eq!(calls, 0);
454 assert_eq!(leftovers, 0);
455 }
456
457 #[test]
458 fn should_call_function_for_each_epoch_for_50_eras_sequentially() {
459 let mut calls = 0;
460
461 let mut previous_epoch_index = -1;
462
463 let leftovers = distribution_storage_fee_to_epochs_map(
464 100000,
465 GENESIS_EPOCH_INDEX,
466 |epoch_index, _| {
467 assert_eq!(epoch_index as i32, previous_epoch_index + 1);
468 previous_epoch_index = epoch_index as i32;
469
470 calls += 1;
471
472 Ok(())
473 },
474 20,
475 )
476 .expect("should distribute storage fee");
477
478 assert_eq!(calls, 1000); assert_eq!(leftovers, 360);
480 }
481 }
482
483 mod distribute_storage_fee_to_epochs_collection {
484 use super::*;
485 use crate::balances::credits::{Creditable, MAX_CREDITS};
486 use crate::fee::SignedCredits;
487
488 #[test]
489 fn should_distribute_max_credits_value_without_overflow() {
490 let storage_fee = MAX_CREDITS;
491
492 let mut credits_per_epochs = SignedCreditsPerEpoch::default();
493
494 let leftovers = distribute_storage_fee_to_epochs_collection(
495 &mut credits_per_epochs,
496 storage_fee,
497 GENESIS_EPOCH_INDEX,
498 20,
499 )
500 .expect("should distribute storage fee");
501
502 assert_eq!(leftovers, 507);
504 }
505
506 #[test]
507 fn should_deterministically_distribute_fees() {
508 let storage_fee = 1000000;
509 let current_epoch_index = 42;
510
511 let mut credits_per_epochs = SignedCreditsPerEpoch::default();
512
513 let leftovers = distribute_storage_fee_to_epochs_collection(
514 &mut credits_per_epochs,
515 storage_fee,
516 current_epoch_index,
517 20,
518 )
519 .expect("should distribute storage fee");
520
521 assert_eq!(leftovers, 180);
523
524 #[rustfmt::skip]
526 let reference_fees: [SignedCredits; 1000] = [
527 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500,
528 2500, 2500, 2500, 2500, 2500, 2500, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400,
529 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2300, 2300,
530 2300, 2300, 2300, 2300, 2300, 2300, 2300, 2300, 2300, 2300, 2300, 2300, 2300, 2300,
531 2300, 2300, 2300, 2300, 2200, 2200, 2200, 2200, 2200, 2200, 2200, 2200, 2200, 2200,
532 2200, 2200, 2200, 2200, 2200, 2200, 2200, 2200, 2200, 2200, 2100, 2100, 2100, 2100,
533 2100, 2100, 2100, 2100, 2100, 2100, 2100, 2100, 2100, 2100, 2100, 2100, 2100, 2100,
534 2100, 2100, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000,
535 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 1925, 1925, 1925, 1925, 1925, 1925,
536 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925,
537 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850,
538 1850, 1850, 1850, 1850, 1850, 1850, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775,
539 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1700, 1700,
540 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700,
541 1700, 1700, 1700, 1700, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625,
542 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1550, 1550, 1550, 1550,
543 1550, 1550, 1550, 1550, 1550, 1550, 1550, 1550, 1550, 1550, 1550, 1550, 1550, 1550,
544 1550, 1550, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475,
545 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1425, 1425, 1425, 1425, 1425, 1425,
546 1425, 1425, 1425, 1425, 1425, 1425, 1425, 1425, 1425, 1425, 1425, 1425, 1425, 1425,
547 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375,
548 1375, 1375, 1375, 1375, 1375, 1375, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325,
549 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1275, 1275,
550 1275, 1275, 1275, 1275, 1275, 1275, 1275, 1275, 1275, 1275, 1275, 1275, 1275, 1275,
551 1275, 1275, 1275, 1275, 1225, 1225, 1225, 1225, 1225, 1225, 1225, 1225, 1225, 1225,
552 1225, 1225, 1225, 1225, 1225, 1225, 1225, 1225, 1225, 1225, 1175, 1175, 1175, 1175,
553 1175, 1175, 1175, 1175, 1175, 1175, 1175, 1175, 1175, 1175, 1175, 1175, 1175, 1175,
554 1175, 1175, 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125,
555 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1075, 1075, 1075, 1075, 1075, 1075,
556 1075, 1075, 1075, 1075, 1075, 1075, 1075, 1075, 1075, 1075, 1075, 1075, 1075, 1075,
557 1025, 1025, 1025, 1025, 1025, 1025, 1025, 1025, 1025, 1025, 1025, 1025, 1025, 1025,
558 1025, 1025, 1025, 1025, 1025, 1025, 975, 975, 975, 975, 975, 975, 975, 975, 975,
559 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 937, 937, 937, 937, 937,
560 937, 937, 937, 937, 937, 937, 937, 937, 937, 937, 937, 937, 937, 937, 937, 900,
561 900, 900, 900, 900, 900, 900, 900, 900, 900, 900, 900, 900, 900, 900, 900, 900,
562 900, 900, 900, 862, 862, 862, 862, 862, 862, 862, 862, 862, 862, 862, 862, 862,
563 862, 862, 862, 862, 862, 862, 862, 825, 825, 825, 825, 825, 825, 825, 825, 825,
564 825, 825, 825, 825, 825, 825, 825, 825, 825, 825, 825, 787, 787, 787, 787, 787,
565 787, 787, 787, 787, 787, 787, 787, 787, 787, 787, 787, 787, 787, 787, 787, 750,
566 750, 750, 750, 750, 750, 750, 750, 750, 750, 750, 750, 750, 750, 750, 750, 750,
567 750, 750, 750, 712, 712, 712, 712, 712, 712, 712, 712, 712, 712, 712, 712, 712,
568 712, 712, 712, 712, 712, 712, 712, 675, 675, 675, 675, 675, 675, 675, 675, 675,
569 675, 675, 675, 675, 675, 675, 675, 675, 675, 675, 675, 637, 637, 637, 637, 637,
570 637, 637, 637, 637, 637, 637, 637, 637, 637, 637, 637, 637, 637, 637, 637, 600,
571 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600,
572 600, 600, 600, 562, 562, 562, 562, 562, 562, 562, 562, 562, 562, 562, 562, 562,
573 562, 562, 562, 562, 562, 562, 562, 525, 525, 525, 525, 525, 525, 525, 525, 525,
574 525, 525, 525, 525, 525, 525, 525, 525, 525, 525, 525, 487, 487, 487, 487, 487,
575 487, 487, 487, 487, 487, 487, 487, 487, 487, 487, 487, 487, 487, 487, 487, 450,
576 450, 450, 450, 450, 450, 450, 450, 450, 450, 450, 450, 450, 450, 450, 450, 450,
577 450, 450, 450, 412, 412, 412, 412, 412, 412, 412, 412, 412, 412, 412, 412, 412,
578 412, 412, 412, 412, 412, 412, 412, 375, 375, 375, 375, 375, 375, 375, 375, 375,
579 375, 375, 375, 375, 375, 375, 375, 375, 375, 375, 375, 337, 337, 337, 337, 337,
580 337, 337, 337, 337, 337, 337, 337, 337, 337, 337, 337, 337, 337, 337, 337, 300,
581 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300,
582 300, 300, 300, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262,
583 262, 262, 262, 262, 262, 262, 262, 237, 237, 237, 237, 237, 237, 237, 237, 237,
584 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 212, 212, 212, 212, 212,
585 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 187,
586 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187,
587 187, 187, 187, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162,
588 162, 162, 162, 162, 162, 162, 162, 137, 137, 137, 137, 137, 137, 137, 137, 137,
589 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 112, 112, 112, 112, 112,
590 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 87,
591 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 62,
592 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62
593 ];
594
595 assert_eq!(
596 credits_per_epochs.clone().into_values().collect::<Vec<_>>(),
597 reference_fees
598 );
599
600 let total_distributed: SignedCredits = credits_per_epochs.values().sum();
601
602 assert_eq!(total_distributed.to_unsigned() + leftovers, storage_fee);
603
604 let leftovers = distribute_storage_fee_to_epochs_collection(
611 &mut credits_per_epochs,
612 storage_fee,
613 current_epoch_index,
614 20,
615 )
616 .expect("should distribute storage fee");
617
618 assert_eq!(
620 credits_per_epochs.into_values().collect::<Vec<_>>(),
621 reference_fees
622 .into_iter()
623 .map(|val| val * 2)
624 .collect::<Vec<_>>()
625 );
626
627 assert_eq!(leftovers, 180);
628 }
629 }
630
631 mod subtract_refunds_from_epoch_credits_collection {
632 use super::*;
633 use crate::balances::credits::Creditable;
634 use crate::fee::SignedCredits;
635
636 #[test]
637 fn should_deduct_refunds_from_collection_since_specific_epoch_start_at_genesis() {
638 let start_epoch_index: EpochIndex = GENESIS_EPOCH_INDEX;
644 const REFUNDED_EPOCH_INDEX: EpochIndex = 42;
645 let original_storage_fee = 1200005;
646
647 let (refund_amount, leftovers) = calculate_storage_fee_refund_amount_and_leftovers(
648 original_storage_fee,
649 start_epoch_index,
650 REFUNDED_EPOCH_INDEX,
651 20,
652 )
653 .expect("should distribute storage fee");
654
655 assert_eq!(refund_amount, 1074120);
656 assert_eq!(leftovers, 5);
657
658 let mut credits_per_epochs = SignedCreditsPerEpoch::default();
659
660 subtract_refunds_from_epoch_credits_collection(
661 &mut credits_per_epochs,
662 refund_amount,
663 start_epoch_index,
664 REFUNDED_EPOCH_INDEX,
665 20,
666 )
667 .expect("should distribute storage fee");
668
669 #[rustfmt::skip]
673 let reference_fees: [SignedCredits;
674 (1000 - REFUNDED_EPOCH_INDEX - 1) as usize] = [-2760, -2760, -2760,
675 -2760, -2760, -2760, -2760, -2760, -2760, -2760, -2760, -2760, -2760, -2760, -2760,
676 -2760, -2760, -2640, -2640, -2640, -2640, -2640, -2640, -2640, -2640, -2640, -2640,
677 -2640, -2640, -2640, -2640, -2640, -2640, -2640, -2640, -2640, -2640, -2520, -2520,
678 -2520, -2520, -2520, -2520, -2520, -2520, -2520, -2520, -2520, -2520, -2520, -2520,
679 -2520, -2520, -2520, -2520, -2520, -2520, -2400, -2400, -2400, -2400, -2400, -2400,
680 -2400, -2400, -2400, -2400, -2400, -2400, -2400, -2400, -2400, -2400, -2400, -2400,
681 -2400, -2400, -2310, -2310, -2310, -2310, -2310, -2310, -2310, -2310, -2310, -2310,
682 -2310, -2310, -2310, -2310, -2310, -2310, -2310, -2310, -2310, -2310, -2220, -2220,
683 -2220, -2220, -2220, -2220, -2220, -2220, -2220, -2220, -2220, -2220, -2220, -2220,
684 -2220, -2220, -2220, -2220, -2220, -2220, -2130, -2130, -2130, -2130, -2130, -2130,
685 -2130, -2130, -2130, -2130, -2130, -2130, -2130, -2130, -2130, -2130, -2130, -2130,
686 -2130, -2130, -2040, -2040, -2040, -2040, -2040, -2040, -2040, -2040, -2040, -2040,
687 -2040, -2040, -2040, -2040, -2040, -2040, -2040, -2040, -2040, -2040, -1950, -1950,
688 -1950, -1950, -1950, -1950, -1950, -1950, -1950, -1950, -1950, -1950, -1950, -1950,
689 -1950, -1950, -1950, -1950, -1950, -1950, -1860, -1860, -1860, -1860, -1860, -1860,
690 -1860, -1860, -1860, -1860, -1860, -1860, -1860, -1860, -1860, -1860, -1860, -1860,
691 -1860, -1860, -1770, -1770, -1770, -1770, -1770, -1770, -1770, -1770, -1770, -1770,
692 -1770, -1770, -1770, -1770, -1770, -1770, -1770, -1770, -1770, -1770, -1710, -1710,
693 -1710, -1710, -1710, -1710, -1710, -1710, -1710, -1710, -1710, -1710, -1710, -1710,
694 -1710, -1710, -1710, -1710, -1710, -1710, -1650, -1650, -1650, -1650, -1650, -1650,
695 -1650, -1650, -1650, -1650, -1650, -1650, -1650, -1650, -1650, -1650, -1650, -1650,
696 -1650, -1650, -1590, -1590, -1590, -1590, -1590, -1590, -1590, -1590, -1590, -1590,
697 -1590, -1590, -1590, -1590, -1590, -1590, -1590, -1590, -1590, -1590, -1530, -1530,
698 -1530, -1530, -1530, -1530, -1530, -1530, -1530, -1530, -1530, -1530, -1530, -1530,
699 -1530, -1530, -1530, -1530, -1530, -1530, -1470, -1470, -1470, -1470, -1470, -1470,
700 -1470, -1470, -1470, -1470, -1470, -1470, -1470, -1470, -1470, -1470, -1470, -1470,
701 -1470, -1470, -1410, -1410, -1410, -1410, -1410, -1410, -1410, -1410, -1410, -1410,
702 -1410, -1410, -1410, -1410, -1410, -1410, -1410, -1410, -1410, -1410, -1350, -1350,
703 -1350, -1350, -1350, -1350, -1350, -1350, -1350, -1350, -1350, -1350, -1350, -1350,
704 -1350, -1350, -1350, -1350, -1350, -1350, -1290, -1290, -1290, -1290, -1290, -1290,
705 -1290, -1290, -1290, -1290, -1290, -1290, -1290, -1290, -1290, -1290, -1290, -1290,
706 -1290, -1290, -1230, -1230, -1230, -1230, -1230, -1230, -1230, -1230, -1230, -1230,
707 -1230, -1230, -1230, -1230, -1230, -1230, -1230, -1230, -1230, -1230, -1170, -1170,
708 -1170, -1170, -1170, -1170, -1170, -1170, -1170, -1170, -1170, -1170, -1170, -1170,
709 -1170, -1170, -1170, -1170, -1170, -1170, -1125, -1125, -1125, -1125, -1125, -1125,
710 -1125, -1125, -1125, -1125, -1125, -1125, -1125, -1125, -1125, -1125, -1125, -1125,
711 -1125, -1125, -1080, -1080, -1080, -1080, -1080, -1080, -1080, -1080, -1080, -1080,
712 -1080, -1080, -1080, -1080, -1080, -1080, -1080, -1080, -1080, -1080, -1035, -1035,
713 -1035, -1035, -1035, -1035, -1035, -1035, -1035, -1035, -1035, -1035, -1035, -1035,
714 -1035, -1035, -1035, -1035, -1035, -1035, -990, -990, -990, -990, -990, -990, -990,
715 -990, -990, -990, -990, -990, -990, -990, -990, -990, -990, -990, -990, -990, -945,
716 -945, -945, -945, -945, -945, -945, -945, -945, -945, -945, -945, -945, -945, -945,
717 -945, -945, -945, -945, -945, -900, -900, -900, -900, -900, -900, -900, -900, -900,
718 -900, -900, -900, -900, -900, -900, -900, -900, -900, -900, -900, -855, -855, -855,
719 -855, -855, -855, -855, -855, -855, -855, -855, -855, -855, -855, -855, -855, -855,
720 -855, -855, -855, -810, -810, -810, -810, -810, -810, -810, -810, -810, -810, -810,
721 -810, -810, -810, -810, -810, -810, -810, -810, -810, -765, -765, -765, -765, -765,
722 -765, -765, -765, -765, -765, -765, -765, -765, -765, -765, -765, -765, -765, -765,
723 -765, -720, -720, -720, -720, -720, -720, -720, -720, -720, -720, -720, -720, -720,
724 -720, -720, -720, -720, -720, -720, -720, -675, -675, -675, -675, -675, -675, -675,
725 -675, -675, -675, -675, -675, -675, -675, -675, -675, -675, -675, -675, -675, -630,
726 -630, -630, -630, -630, -630, -630, -630, -630, -630, -630, -630, -630, -630, -630,
727 -630, -630, -630, -630, -630, -585, -585, -585, -585, -585, -585, -585, -585, -585,
728 -585, -585, -585, -585, -585, -585, -585, -585, -585, -585, -585, -540, -540, -540,
729 -540, -540, -540, -540, -540, -540, -540, -540, -540, -540, -540, -540, -540, -540,
730 -540, -540, -540, -495, -495, -495, -495, -495, -495, -495, -495, -495, -495, -495,
731 -495, -495, -495, -495, -495, -495, -495, -495, -495, -450, -450, -450, -450, -450,
732 -450, -450, -450, -450, -450, -450, -450, -450, -450, -450, -450, -450, -450, -450,
733 -450, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405,
734 -405, -405, -405, -405, -405, -405, -405, -360, -360, -360, -360, -360, -360, -360,
735 -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -315,
736 -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315,
737 -315, -315, -315, -315, -315, -285, -285, -285, -285, -285, -285, -285, -285, -285,
738 -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -255, -255, -255,
739 -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255,
740 -255, -255, -255, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225,
741 -225, -225, -225, -225, -225, -225, -225, -225, -225, -195, -195, -195, -195, -195,
742 -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195,
743 -195, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165,
744 -165, -165, -165, -165, -165, -165, -165, -135, -135, -135, -135, -135, -135, -135,
745 -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -105,
746 -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105,
747 -105, -105, -105, -105, -105, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75,
748 -75, -75, -75, -75, -75, -75, -75, -75, -75];
749
750 assert_eq!(
751 credits_per_epochs.clone().into_values().collect::<Vec<_>>(),
752 reference_fees
753 );
754
755 let total_distributed: SignedCredits = credits_per_epochs.values().sum();
756
757 assert_eq!(total_distributed.to_unsigned(), refund_amount);
758 }
759
760 #[test]
787 fn should_not_halt_when_refund_distributed_as_window_boundary_is_crossed() {
788 const EPOCHS_PER_ERA: u16 = 40; const WRITE_EPOCH: EpochIndex = 0;
790
791 let window_end_epoch = WRITE_EPOCH + EPOCHS_PER_ERA * PERPETUAL_STORAGE_ERAS; let removal_epoch = window_end_epoch - 2; let distribution_epoch = removal_epoch + 1; let original_storage_fee: Credits = 10_000_000_000;
807
808 let (refund_amount, _leftovers) = calculate_storage_fee_refund_amount_and_leftovers(
809 original_storage_fee,
810 WRITE_EPOCH,
811 removal_epoch,
812 EPOCHS_PER_ERA,
813 )
814 .expect("refund amount computation should succeed");
815
816 assert!(
817 refund_amount > 0,
818 "removing data before the final in-window epoch ({}) must leave a \
819 non-zero refund; got {refund_amount}",
820 window_end_epoch - 1,
821 );
822
823 let mut credits_per_epochs = SignedCreditsPerEpoch::default();
824
825 subtract_refunds_from_epoch_credits_collection(
831 &mut credits_per_epochs,
832 refund_amount,
833 WRITE_EPOCH,
834 distribution_epoch,
835 EPOCHS_PER_ERA,
836 )
837 .expect("refund distribution must not halt the chain at the window boundary");
838
839 let entries: Vec<(EpochIndex, SignedCredits)> =
844 credits_per_epochs.into_iter().collect();
845 assert_eq!(
846 entries,
847 vec![(distribution_epoch, -(refund_amount as SignedCredits))],
848 "the full refund should be clawed back from only the current epoch",
849 );
850 }
851
852 #[test]
853 fn should_deduct_refunds_from_collection_start_epoch_doesnt_matter_check() {
854 for start_epoch_index in 0..150 {
855 let current_epoch_index_where_refund_occurred: EpochIndex = start_epoch_index + 14;
856
857 let original_storage_fee = 3405507;
858 let (refund_amount, leftovers) = calculate_storage_fee_refund_amount_and_leftovers(
859 original_storage_fee,
860 start_epoch_index,
861 current_epoch_index_where_refund_occurred,
862 20,
863 )
864 .expect("should distribute storage fee");
865
866 assert_eq!(refund_amount, 3277305);
867 assert_eq!(leftovers, 507);
868
869 let multiplier = original_removed_credits_multiplier_from(
870 start_epoch_index,
871 current_epoch_index_where_refund_occurred + 1,
872 20,
873 )
874 .expect("multiplier within perpetual storage window");
875
876 assert!(
881 (Decimal::from(refund_amount) * multiplier)
882 .abs_sub(&Decimal::from(original_storage_fee - leftovers))
883 < dec!(100)
884 );
885
886 assert!(
888 (Decimal::from(refund_amount) * multiplier)
889 < Decimal::from(original_storage_fee - leftovers)
890 );
891
892 let mut credits_per_epochs = SignedCreditsPerEpoch::default();
893
894 subtract_refunds_from_epoch_credits_collection(
895 &mut credits_per_epochs,
896 refund_amount,
897 start_epoch_index,
898 current_epoch_index_where_refund_occurred,
899 20,
900 )
901 .expect("should distribute storage fee");
902 #[rustfmt::skip]
906 let reference_fees: Vec<SignedCredits> =
907 vec![-525, -8512, -8512, -8512, -8512, -8512, -8171, -8171, -8171, -8171, -8171, -8171,
908 -8171, -8171, -8171, -8171, -8171, -8171, -8171, -8171, -8171, -8171, -8171,
909 -8171, -8171, -8171, -7831, -7831, -7831, -7831, -7831, -7831, -7831, -7831,
910 -7831, -7831, -7831, -7831, -7831, -7831, -7831, -7831, -7831, -7831, -7831,
911 -7831, -7490, -7490, -7490, -7490, -7490, -7490, -7490, -7490, -7490, -7490,
912 -7490, -7490, -7490, -7490, -7490, -7490, -7490, -7490, -7490, -7490, -7150,
913 -7150, -7150, -7150, -7150, -7150, -7150, -7150, -7150, -7150, -7150, -7150,
914 -7150, -7150, -7150, -7150, -7150, -7150, -7150, -7150, -6809, -6809, -6809,
915 -6809, -6809, -6809, -6809, -6809, -6809, -6809, -6809, -6809, -6809, -6809,
916 -6809, -6809, -6809, -6809, -6809, -6809, -6554, -6554, -6554, -6554, -6554,
917 -6554, -6554, -6554, -6554, -6554, -6554, -6554, -6554, -6554, -6554, -6554,
918 -6554, -6554, -6554, -6554, -6299, -6299, -6299, -6299, -6299, -6299, -6299,
919 -6299, -6299, -6299, -6299, -6299, -6299, -6299, -6299, -6299, -6299, -6299,
920 -6299, -6299, -6043, -6043, -6043, -6043, -6043, -6043, -6043, -6043, -6043,
921 -6043, -6043, -6043, -6043, -6043, -6043, -6043, -6043, -6043, -6043, -6043,
922 -5788, -5788, -5788, -5788, -5788, -5788, -5788, -5788, -5788, -5788, -5788,
923 -5788, -5788, -5788, -5788, -5788, -5788, -5788, -5788, -5788, -5533, -5533,
924 -5533, -5533, -5533, -5533, -5533, -5533, -5533, -5533, -5533, -5533, -5533,
925 -5533, -5533, -5533, -5533, -5533, -5533, -5533, -5277, -5277, -5277, -5277,
926 -5277, -5277, -5277, -5277, -5277, -5277, -5277, -5277, -5277, -5277, -5277,
927 -5277, -5277, -5277, -5277, -5277, -5022, -5022, -5022, -5022, -5022, -5022,
928 -5022, -5022, -5022, -5022, -5022, -5022, -5022, -5022, -5022, -5022, -5022,
929 -5022, -5022, -5022, -4852, -4852, -4852, -4852, -4852, -4852, -4852, -4852,
930 -4852, -4852, -4852, -4852, -4852, -4852, -4852, -4852, -4852, -4852, -4852,
931 -4852, -4681, -4681, -4681, -4681, -4681, -4681, -4681, -4681, -4681, -4681,
932 -4681, -4681, -4681, -4681, -4681, -4681, -4681, -4681, -4681, -4681, -4511,
933 -4511, -4511, -4511, -4511, -4511, -4511, -4511, -4511, -4511, -4511, -4511,
934 -4511, -4511, -4511, -4511, -4511, -4511, -4511, -4511, -4341, -4341, -4341,
935 -4341, -4341, -4341, -4341, -4341, -4341, -4341, -4341, -4341, -4341, -4341,
936 -4341, -4341, -4341, -4341, -4341, -4341, -4171, -4171, -4171, -4171, -4171,
937 -4171, -4171, -4171, -4171, -4171, -4171, -4171, -4171, -4171, -4171, -4171,
938 -4171, -4171, -4171, -4171, -4000, -4000, -4000, -4000, -4000, -4000, -4000,
939 -4000, -4000, -4000, -4000, -4000, -4000, -4000, -4000, -4000, -4000, -4000,
940 -4000, -4000, -3830, -3830, -3830, -3830, -3830, -3830, -3830, -3830, -3830,
941 -3830, -3830, -3830, -3830, -3830, -3830, -3830, -3830, -3830, -3830, -3830,
942 -3660, -3660, -3660, -3660, -3660, -3660, -3660, -3660, -3660, -3660, -3660,
943 -3660, -3660, -3660, -3660, -3660, -3660, -3660, -3660, -3660, -3490, -3490,
944 -3490, -3490, -3490, -3490, -3490, -3490, -3490, -3490, -3490, -3490, -3490,
945 -3490, -3490, -3490, -3490, -3490, -3490, -3490, -3319, -3319, -3319, -3319,
946 -3319, -3319, -3319, -3319, -3319, -3319, -3319, -3319, -3319, -3319, -3319,
947 -3319, -3319, -3319, -3319, -3319, -3192, -3192, -3192, -3192, -3192, -3192,
948 -3192, -3192, -3192, -3192, -3192, -3192, -3192, -3192, -3192, -3192, -3192,
949 -3192, -3192, -3192, -3064, -3064, -3064, -3064, -3064, -3064, -3064, -3064,
950 -3064, -3064, -3064, -3064, -3064, -3064, -3064, -3064, -3064, -3064, -3064,
951 -3064, -2936, -2936, -2936, -2936, -2936, -2936, -2936, -2936, -2936, -2936,
952 -2936, -2936, -2936, -2936, -2936, -2936, -2936, -2936, -2936, -2936, -2809,
953 -2809, -2809, -2809, -2809, -2809, -2809, -2809, -2809, -2809, -2809, -2809,
954 -2809, -2809, -2809, -2809, -2809, -2809, -2809, -2809, -2681, -2681, -2681,
955 -2681, -2681, -2681, -2681, -2681, -2681, -2681, -2681, -2681, -2681, -2681,
956 -2681, -2681, -2681, -2681, -2681, -2681, -2553, -2553, -2553, -2553, -2553,
957 -2553, -2553, -2553, -2553, -2553, -2553, -2553, -2553, -2553, -2553, -2553,
958 -2553, -2553, -2553, -2553, -2426, -2426, -2426, -2426, -2426, -2426, -2426,
959 -2426, -2426, -2426, -2426, -2426, -2426, -2426, -2426, -2426, -2426, -2426,
960 -2426, -2426, -2298, -2298, -2298, -2298, -2298, -2298, -2298, -2298, -2298,
961 -2298, -2298, -2298, -2298, -2298, -2298, -2298, -2298, -2298, -2298, -2298,
962 -2170, -2170, -2170, -2170, -2170, -2170, -2170, -2170, -2170, -2170, -2170,
963 -2170, -2170, -2170, -2170, -2170, -2170, -2170, -2170, -2170, -2042, -2042,
964 -2042, -2042, -2042, -2042, -2042, -2042, -2042, -2042, -2042, -2042, -2042,
965 -2042, -2042, -2042, -2042, -2042, -2042, -2042, -1915, -1915, -1915, -1915,
966 -1915, -1915, -1915, -1915, -1915, -1915, -1915, -1915, -1915, -1915, -1915,
967 -1915, -1915, -1915, -1915, -1915, -1787, -1787, -1787, -1787, -1787, -1787,
968 -1787, -1787, -1787, -1787, -1787, -1787, -1787, -1787, -1787, -1787, -1787,
969 -1787, -1787, -1787, -1659, -1659, -1659, -1659, -1659, -1659, -1659, -1659,
970 -1659, -1659, -1659, -1659, -1659, -1659, -1659, -1659, -1659, -1659, -1659,
971 -1659, -1532, -1532, -1532, -1532, -1532, -1532, -1532, -1532, -1532, -1532,
972 -1532, -1532, -1532, -1532, -1532, -1532, -1532, -1532, -1532, -1532, -1404,
973 -1404, -1404, -1404, -1404, -1404, -1404, -1404, -1404, -1404, -1404, -1404,
974 -1404, -1404, -1404, -1404, -1404, -1404, -1404, -1404, -1276, -1276, -1276,
975 -1276, -1276, -1276, -1276, -1276, -1276, -1276, -1276, -1276, -1276, -1276,
976 -1276, -1276, -1276, -1276, -1276, -1276, -1149, -1149, -1149, -1149, -1149,
977 -1149, -1149, -1149, -1149, -1149, -1149, -1149, -1149, -1149, -1149, -1149,
978 -1149, -1149, -1149, -1149, -1021, -1021, -1021, -1021, -1021, -1021, -1021,
979 -1021, -1021, -1021, -1021, -1021, -1021, -1021, -1021, -1021, -1021, -1021,
980 -1021, -1021, -893, -893, -893, -893, -893, -893, -893, -893, -893, -893,
981 -893, -893, -893, -893, -893, -893, -893, -893, -893, -893, -808, -808, -808,
982 -808, -808, -808, -808, -808, -808, -808, -808, -808, -808, -808, -808, -808,
983 -808, -808, -808, -808, -723, -723, -723, -723, -723, -723, -723, -723, -723,
984 -723, -723, -723, -723, -723, -723, -723, -723, -723, -723, -723, -638, -638,
985 -638, -638, -638, -638, -638, -638, -638, -638, -638, -638, -638, -638, -638,
986 -638, -638, -638, -638, -638, -553, -553, -553, -553, -553, -553, -553, -553,
987 -553, -553, -553, -553, -553, -553, -553, -553, -553, -553, -553, -553, -468,
988 -468, -468, -468, -468, -468, -468, -468, -468, -468, -468, -468, -468, -468,
989 -468, -468, -468, -468, -468, -468, -383, -383, -383, -383, -383, -383, -383,
990 -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383,
991 -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297,
992 -297, -297, -297, -297, -297, -297, -297, -212, -212, -212, -212, -212, -212,
993 -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212,
994 -212];
995
996 assert_eq!(
997 credits_per_epochs.clone().into_values().collect::<Vec<_>>(),
998 reference_fees
999 );
1000
1001 let total_distributed: SignedCredits = credits_per_epochs.values().sum();
1002
1003 assert_eq!(total_distributed.to_unsigned(), refund_amount);
1004 }
1005 }
1006
1007 #[test]
1008 fn should_deduct_refunds_from_two_collection_since_specific_epoch() {
1009 const CURRENT_EPOCH_INDEX_WHERE_REFUND_OCCURRED: EpochIndex = 42;
1010 let mut credits_per_epochs = SignedCreditsPerEpoch::default();
1011
1012 let first_start_epoch_index: EpochIndex = GENESIS_EPOCH_INDEX;
1020
1021 let first_original_storage_fee = 1200005;
1022 let (first_refund_amount, leftovers) =
1023 calculate_storage_fee_refund_amount_and_leftovers(
1024 first_original_storage_fee,
1025 first_start_epoch_index,
1026 CURRENT_EPOCH_INDEX_WHERE_REFUND_OCCURRED,
1027 20,
1028 )
1029 .expect("should distribute storage fee");
1030
1031 assert_eq!(first_refund_amount, 1074120);
1032 assert_eq!(leftovers, 5);
1033
1034 subtract_refunds_from_epoch_credits_collection(
1035 &mut credits_per_epochs,
1036 first_refund_amount,
1037 first_start_epoch_index,
1038 CURRENT_EPOCH_INDEX_WHERE_REFUND_OCCURRED,
1039 20,
1040 )
1041 .expect("should distribute storage fee");
1042
1043 const SECOND_START_EPOCH_INDEX: EpochIndex = 28;
1050
1051 let second_original_storage_fee = 3405507;
1052 let (second_refund_amount, leftovers) =
1053 calculate_storage_fee_refund_amount_and_leftovers(
1054 second_original_storage_fee,
1055 SECOND_START_EPOCH_INDEX,
1056 CURRENT_EPOCH_INDEX_WHERE_REFUND_OCCURRED,
1057 20,
1058 )
1059 .expect("should distribute storage fee");
1060
1061 assert_eq!(second_refund_amount, 3277305);
1062 assert_eq!(leftovers, 507);
1063
1064 let multiplier = original_removed_credits_multiplier_from(
1065 SECOND_START_EPOCH_INDEX,
1066 CURRENT_EPOCH_INDEX_WHERE_REFUND_OCCURRED + 1,
1067 20,
1068 )
1069 .expect("multiplier within perpetual storage window");
1070
1071 assert!(
1076 (Decimal::from(second_refund_amount) * multiplier)
1077 .abs_sub(&Decimal::from(second_original_storage_fee - leftovers))
1078 < dec!(100)
1079 );
1080
1081 assert!(
1083 (Decimal::from(second_refund_amount) * multiplier)
1084 < Decimal::from(second_original_storage_fee - leftovers)
1085 );
1086
1087 subtract_refunds_from_epoch_credits_collection(
1088 &mut credits_per_epochs,
1089 second_refund_amount,
1090 SECOND_START_EPOCH_INDEX,
1091 CURRENT_EPOCH_INDEX_WHERE_REFUND_OCCURRED,
1092 20,
1093 )
1094 .expect("should distribute storage fee");
1095 #[rustfmt::skip]
1099 let reference_fees: [SignedCredits;
1100 (SECOND_START_EPOCH_INDEX + 1000 - CURRENT_EPOCH_INDEX_WHERE_REFUND_OCCURRED) as usize] =
1101 [-525, -11272, -11272, -11272, -11272, -11272, -10931, -10931, -10931, -10931,
1102 -10931, -10931, -10931, -10931, -10931, -10931, -10931, -10931, -10811, -10811,
1103 -10811, -10811, -10811, -10811, -10811, -10811, -10471, -10471, -10471, -10471,
1104 -10471, -10471, -10471, -10471, -10471, -10471, -10471, -10471, -10351, -10351,
1105 -10351, -10351, -10351, -10351, -10351, -10351, -10010, -10010, -10010, -10010,
1106 -10010, -10010, -10010, -10010, -10010, -10010, -10010, -10010, -9890, -9890,
1107 -9890, -9890, -9890, -9890, -9890, -9890, -9550, -9550, -9550, -9550, -9550,
1108 -9550, -9550, -9550, -9550, -9550, -9550, -9550, -9460, -9460, -9460, -9460,
1109 -9460, -9460, -9460, -9460, -9119, -9119, -9119, -9119, -9119, -9119, -9119,
1110 -9119, -9119, -9119, -9119, -9119, -9029, -9029, -9029, -9029, -9029, -9029,
1111 -9029, -9029, -8774, -8774, -8774, -8774, -8774, -8774, -8774, -8774, -8774,
1112 -8774, -8774, -8774, -8684, -8684, -8684, -8684, -8684, -8684, -8684, -8684,
1113 -8429, -8429, -8429, -8429, -8429, -8429, -8429, -8429, -8429, -8429, -8429,
1114 -8429, -8339, -8339, -8339, -8339, -8339, -8339, -8339, -8339, -8083, -8083,
1115 -8083, -8083, -8083, -8083, -8083, -8083, -8083, -8083, -8083, -8083, -7993,
1116 -7993, -7993, -7993, -7993, -7993, -7993, -7993, -7738, -7738, -7738, -7738,
1117 -7738, -7738, -7738, -7738, -7738, -7738, -7738, -7738, -7648, -7648, -7648,
1118 -7648, -7648, -7648, -7648, -7648, -7393, -7393, -7393, -7393, -7393, -7393,
1119 -7393, -7393, -7393, -7393, -7393, -7393, -7303, -7303, -7303, -7303, -7303,
1120 -7303, -7303, -7303, -7047, -7047, -7047, -7047, -7047, -7047, -7047, -7047,
1121 -7047, -7047, -7047, -7047, -6987, -6987, -6987, -6987, -6987, -6987, -6987,
1122 -6987, -6732, -6732, -6732, -6732, -6732, -6732, -6732, -6732, -6732, -6732,
1123 -6732, -6732, -6672, -6672, -6672, -6672, -6672, -6672, -6672, -6672, -6502,
1124 -6502, -6502, -6502, -6502, -6502, -6502, -6502, -6502, -6502, -6502, -6502,
1125 -6442, -6442, -6442, -6442, -6442, -6442, -6442, -6442, -6271, -6271, -6271,
1126 -6271, -6271, -6271, -6271, -6271, -6271, -6271, -6271, -6271, -6211, -6211,
1127 -6211, -6211, -6211, -6211, -6211, -6211, -6041, -6041, -6041, -6041, -6041,
1128 -6041, -6041, -6041, -6041, -6041, -6041, -6041, -5981, -5981, -5981, -5981,
1129 -5981, -5981, -5981, -5981, -5811, -5811, -5811, -5811, -5811, -5811, -5811,
1130 -5811, -5811, -5811, -5811, -5811, -5751, -5751, -5751, -5751, -5751, -5751,
1131 -5751, -5751, -5581, -5581, -5581, -5581, -5581, -5581, -5581, -5581, -5581,
1132 -5581, -5581, -5581, -5521, -5521, -5521, -5521, -5521, -5521, -5521, -5521,
1133 -5350, -5350, -5350, -5350, -5350, -5350, -5350, -5350, -5350, -5350, -5350,
1134 -5350, -5290, -5290, -5290, -5290, -5290, -5290, -5290, -5290, -5120, -5120,
1135 -5120, -5120, -5120, -5120, -5120, -5120, -5120, -5120, -5120, -5120, -5060,
1136 -5060, -5060, -5060, -5060, -5060, -5060, -5060, -4890, -4890, -4890, -4890,
1137 -4890, -4890, -4890, -4890, -4890, -4890, -4890, -4890, -4830, -4830, -4830,
1138 -4830, -4830, -4830, -4830, -4830, -4660, -4660, -4660, -4660, -4660, -4660,
1139 -4660, -4660, -4660, -4660, -4660, -4660, -4615, -4615, -4615, -4615, -4615,
1140 -4615, -4615, -4615, -4444, -4444, -4444, -4444, -4444, -4444, -4444, -4444,
1141 -4444, -4444, -4444, -4444, -4399, -4399, -4399, -4399, -4399, -4399, -4399,
1142 -4399, -4272, -4272, -4272, -4272, -4272, -4272, -4272, -4272, -4272, -4272,
1143 -4272, -4272, -4227, -4227, -4227, -4227, -4227, -4227, -4227, -4227, -4099,
1144 -4099, -4099, -4099, -4099, -4099, -4099, -4099, -4099, -4099, -4099, -4099,
1145 -4054, -4054, -4054, -4054, -4054, -4054, -4054, -4054, -3926, -3926, -3926,
1146 -3926, -3926, -3926, -3926, -3926, -3926, -3926, -3926, -3926, -3881, -3881,
1147 -3881, -3881, -3881, -3881, -3881, -3881, -3754, -3754, -3754, -3754, -3754,
1148 -3754, -3754, -3754, -3754, -3754, -3754, -3754, -3709, -3709, -3709, -3709,
1149 -3709, -3709, -3709, -3709, -3581, -3581, -3581, -3581, -3581, -3581, -3581,
1150 -3581, -3581, -3581, -3581, -3581, -3536, -3536, -3536, -3536, -3536, -3536,
1151 -3536, -3536, -3408, -3408, -3408, -3408, -3408, -3408, -3408, -3408, -3408,
1152 -3408, -3408, -3408, -3363, -3363, -3363, -3363, -3363, -3363, -3363, -3363,
1153 -3236, -3236, -3236, -3236, -3236, -3236, -3236, -3236, -3236, -3236, -3236,
1154 -3236, -3191, -3191, -3191, -3191, -3191, -3191, -3191, -3191, -3063, -3063,
1155 -3063, -3063, -3063, -3063, -3063, -3063, -3063, -3063, -3063, -3063, -3018,
1156 -3018, -3018, -3018, -3018, -3018, -3018, -3018, -2890, -2890, -2890, -2890,
1157 -2890, -2890, -2890, -2890, -2890, -2890, -2890, -2890, -2845, -2845, -2845,
1158 -2845, -2845, -2845, -2845, -2845, -2717, -2717, -2717, -2717, -2717, -2717,
1159 -2717, -2717, -2717, -2717, -2717, -2717, -2672, -2672, -2672, -2672, -2672,
1160 -2672, -2672, -2672, -2545, -2545, -2545, -2545, -2545, -2545, -2545, -2545,
1161 -2545, -2545, -2545, -2545, -2500, -2500, -2500, -2500, -2500, -2500, -2500,
1162 -2500, -2372, -2372, -2372, -2372, -2372, -2372, -2372, -2372, -2372, -2372,
1163 -2372, -2372, -2327, -2327, -2327, -2327, -2327, -2327, -2327, -2327, -2199,
1164 -2199, -2199, -2199, -2199, -2199, -2199, -2199, -2199, -2199, -2199, -2199,
1165 -2154, -2154, -2154, -2154, -2154, -2154, -2154, -2154, -2027, -2027, -2027,
1166 -2027, -2027, -2027, -2027, -2027, -2027, -2027, -2027, -2027, -1982, -1982,
1167 -1982, -1982, -1982, -1982, -1982, -1982, -1854, -1854, -1854, -1854, -1854,
1168 -1854, -1854, -1854, -1854, -1854, -1854, -1854, -1809, -1809, -1809, -1809,
1169 -1809, -1809, -1809, -1809, -1681, -1681, -1681, -1681, -1681, -1681, -1681,
1170 -1681, -1681, -1681, -1681, -1681, -1636, -1636, -1636, -1636, -1636, -1636,
1171 -1636, -1636, -1509, -1509, -1509, -1509, -1509, -1509, -1509, -1509, -1509,
1172 -1509, -1509, -1509, -1464, -1464, -1464, -1464, -1464, -1464, -1464, -1464,
1173 -1336, -1336, -1336, -1336, -1336, -1336, -1336, -1336, -1336, -1336, -1336,
1174 -1336, -1306, -1306, -1306, -1306, -1306, -1306, -1306, -1306, -1178, -1178,
1175 -1178, -1178, -1178, -1178, -1178, -1178, -1178, -1178, -1178, -1178, -1148,
1176 -1148, -1148, -1148, -1148, -1148, -1148, -1148, -1063, -1063, -1063, -1063,
1177 -1063, -1063, -1063, -1063, -1063, -1063, -1063, -1063, -1033, -1033, -1033,
1178 -1033, -1033, -1033, -1033, -1033, -948, -948, -948, -948, -948, -948, -948,
1179 -948, -948, -948, -948, -948, -918, -918, -918, -918, -918, -918, -918, -918,
1180 -833, -833, -833, -833, -833, -833, -833, -833, -833, -833, -833, -833, -803,
1181 -803, -803, -803, -803, -803, -803, -803, -718, -718, -718, -718, -718, -718,
1182 -718, -718, -718, -718, -718, -718, -688, -688, -688, -688, -688, -688, -688,
1183 -688, -603, -603, -603, -603, -603, -603, -603, -603, -603, -603, -603, -603,
1184 -573, -573, -573, -573, -573, -573, -573, -573, -488, -488, -488, -488, -488,
1185 -488, -488, -488, -488, -488, -488, -488, -458, -458, -458, -458, -458, -458,
1186 -458, -458, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372,
1187 -372, -297, -297, -297, -297, -297, -297, -297, -297, -212, -212, -212, -212,
1188 -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212,
1189 -212, -212, -212];
1190
1191 assert_eq!(
1192 credits_per_epochs.clone().into_values().collect::<Vec<_>>(),
1193 reference_fees
1194 );
1195
1196 let total_distributed: SignedCredits = credits_per_epochs.values().sum();
1197
1198 assert_eq!(
1199 total_distributed.to_unsigned(),
1200 first_refund_amount + second_refund_amount
1201 );
1202 }
1203 }
1204
1205 mod calculate_storage_fee_refund_amount_and_leftovers {
1206 use super::*;
1207
1208 #[test]
1209 fn should_calculate_amount_and_leftovers() {
1210 let storage_fee = 10000;
1211
1212 let (amount, leftovers) = calculate_storage_fee_refund_amount_and_leftovers(
1213 storage_fee,
1214 GENESIS_EPOCH_INDEX + 1,
1215 2,
1216 20,
1217 )
1218 .expect("should distribute storage fee");
1219
1220 let first_two_epochs_amount = 50;
1221
1222 assert_eq!(leftovers, 400);
1223 assert_eq!(amount, storage_fee - leftovers - first_two_epochs_amount);
1224 }
1225
1226 #[test]
1227 fn should_return_zero_amount_and_zero_leftovers_for_zero_storage_fee() {
1228 let (amount, leftovers) =
1229 calculate_storage_fee_refund_amount_and_leftovers(0, GENESIS_EPOCH_INDEX, 10, 20)
1230 .expect("should handle zero storage fee");
1231
1232 assert_eq!(amount, 0);
1233 assert_eq!(leftovers, 0);
1234 }
1235
1236 #[test]
1237 fn should_return_zero_refund_when_start_epoch_equals_current_epoch() {
1238 let storage_fee = 1000000;
1241 let epoch = 0;
1242
1243 let (amount, leftovers) =
1244 calculate_storage_fee_refund_amount_and_leftovers(storage_fee, epoch, epoch, 20)
1245 .expect("should distribute storage fee");
1246
1247 assert_eq!(amount, storage_fee - 2500 - leftovers);
1250 }
1251
1252 #[test]
1253 fn should_calculate_correctly_with_non_genesis_start() {
1254 let storage_fee = 500000;
1255 let start = 100;
1256 let current = 110;
1257
1258 let (amount, leftovers) =
1259 calculate_storage_fee_refund_amount_and_leftovers(storage_fee, start, current, 20)
1260 .expect("should distribute storage fee");
1261
1262 assert_eq!(
1264 amount + (storage_fee - amount - leftovers) + leftovers,
1265 storage_fee
1266 );
1267 assert!(amount < storage_fee);
1269 assert!(leftovers < storage_fee);
1270 }
1271
1272 #[test]
1273 fn should_handle_large_epoch_gap() {
1274 let storage_fee = 10_000_000;
1276 let start = 0;
1277 let current = 500; let (amount, leftovers) =
1280 calculate_storage_fee_refund_amount_and_leftovers(storage_fee, start, current, 20)
1281 .expect("should handle large epoch gap");
1282
1283 assert!(amount < storage_fee / 2);
1285 assert!(leftovers < storage_fee);
1286 }
1287 }
1288
1289 mod additional_original_removed_credits_multiplier_from {
1290 use super::*;
1291
1292 #[test]
1293 fn should_create_multiplier_of_one_when_no_epochs_have_passed() {
1294 let multiplier = original_removed_credits_multiplier_from(0, 0, 20)
1297 .expect("multiplier within perpetual storage window");
1298 assert_eq!(multiplier, dec!(1));
1299 }
1300
1301 #[test]
1302 fn should_increase_multiplier_as_more_epochs_pass() {
1303 let m1 = original_removed_credits_multiplier_from(0, 5, 20)
1304 .expect("multiplier within perpetual storage window");
1305 let m2 = original_removed_credits_multiplier_from(0, 10, 20)
1306 .expect("multiplier within perpetual storage window");
1307 let m3 = original_removed_credits_multiplier_from(0, 19, 20)
1308 .expect("multiplier within perpetual storage window");
1309
1310 assert!(m1 < m2);
1312 assert!(m2 < m3);
1313 }
1314
1315 #[test]
1316 fn should_handle_era_boundary_crossing() {
1317 let m_at_boundary = original_removed_credits_multiplier_from(0, 20, 20)
1319 .expect("multiplier within perpetual storage window");
1320 let m_before_boundary = original_removed_credits_multiplier_from(0, 19, 20)
1321 .expect("multiplier within perpetual storage window");
1322 let m_after_boundary = original_removed_credits_multiplier_from(0, 21, 20)
1323 .expect("multiplier within perpetual storage window");
1324
1325 assert!(m_at_boundary > m_before_boundary);
1327 assert!(m_after_boundary > m_at_boundary);
1328 }
1329
1330 #[test]
1331 fn should_handle_different_epochs_per_era() {
1332 let m_40 = original_removed_credits_multiplier_from(0, 40, 40)
1334 .expect("multiplier within perpetual storage window");
1335 let m_20 = original_removed_credits_multiplier_from(0, 20, 20)
1337 .expect("multiplier within perpetual storage window");
1338
1339 assert_eq!(m_40, m_20);
1341 }
1342
1343 #[test]
1344 fn should_produce_same_multiplier_regardless_of_absolute_epoch_offset() {
1345 let m1 = original_removed_credits_multiplier_from(0, 15, 20)
1347 .expect("multiplier within perpetual storage window");
1348 let m2 = original_removed_credits_multiplier_from(100, 115, 20)
1349 .expect("multiplier within perpetual storage window");
1350 let m3 = original_removed_credits_multiplier_from(5000, 5015, 20)
1351 .expect("multiplier within perpetual storage window");
1352
1353 assert_eq!(m1, m2);
1354 assert_eq!(m2, m3);
1355 }
1356
1357 #[test]
1358 fn should_return_error_instead_of_panicking_when_window_fully_elapsed() {
1359 let result = original_removed_credits_multiplier_from(0, 1000, 20);
1366 assert!(
1367 matches!(result, Err(ProtocolError::DivideByZero(_))),
1368 "expected DivideByZero error once the window is fully elapsed, got {:?}",
1369 result
1370 );
1371
1372 let restored = restore_original_removed_credits_amount(dec!(1_000_000), 0, 1000, 20);
1374 assert!(
1375 matches!(restored, Err(ProtocolError::DivideByZero(_))),
1376 "restore_original_removed_credits_amount must propagate the error, got {:?}",
1377 restored
1378 );
1379 }
1380
1381 #[test]
1382 fn should_return_error_when_repayment_epoch_precedes_start() {
1383 let result = original_removed_credits_multiplier_from(10, 5, 20);
1386 assert!(
1387 matches!(result, Err(ProtocolError::Overflow(_))),
1388 "expected Overflow error on underflowing epoch difference, got {:?}",
1389 result
1390 );
1391 }
1392 }
1393
1394 mod additional_restore_original_removed_credits_amount {
1395 use super::*;
1396
1397 #[test]
1398 fn should_restore_to_original_when_no_epochs_passed() {
1399 let refund = dec!(1000000);
1401 let restored = restore_original_removed_credits_amount(refund, 0, 0, 20)
1402 .expect("should not overflow");
1403 assert_eq!(restored, refund);
1404 }
1405
1406 #[test]
1407 fn should_increase_amount_when_epochs_have_passed() {
1408 let refund = dec!(500000);
1410 let restored = restore_original_removed_credits_amount(refund, 0, 10, 20)
1411 .expect("should not overflow");
1412 assert!(restored > refund);
1413 }
1414
1415 #[test]
1416 fn should_handle_zero_refund_amount() {
1417 let restored = restore_original_removed_credits_amount(dec!(0), 0, 10, 20)
1418 .expect("should handle zero");
1419 assert_eq!(restored, dec!(0));
1420 }
1421 }
1422
1423 mod additional_refund_storage_fee_to_epochs_map {
1424 use super::*;
1425
1426 #[test]
1427 fn should_return_zero_leftovers_for_zero_storage_fee() {
1428 let leftovers = refund_storage_fee_to_epochs_map(0, 0, 1, |_, _| Ok(()), 20)
1429 .expect("should handle zero");
1430 assert_eq!(leftovers, 0);
1431 }
1432
1433 #[test]
1434 fn should_skip_epochs_before_skip_until_index() {
1435 let storage_fee = 1000000u64;
1436 let start = 0u16;
1437 let skip_until = 10u16;
1438
1439 let mut min_epoch_seen = u16::MAX;
1440
1441 let _leftovers = refund_storage_fee_to_epochs_map(
1442 storage_fee,
1443 start,
1444 skip_until,
1445 |epoch_index, _amount| {
1446 if epoch_index < min_epoch_seen {
1447 min_epoch_seen = epoch_index;
1448 }
1449 Ok(())
1450 },
1451 20,
1452 )
1453 .expect("should distribute refund");
1454
1455 assert!(min_epoch_seen >= skip_until);
1457 }
1458
1459 #[test]
1460 fn should_distribute_to_single_remaining_epoch_in_era() {
1461 let storage_fee = 100000u64;
1464
1465 let mut epoch_count = 0u32;
1466
1467 let leftovers = refund_storage_fee_to_epochs_map(
1468 storage_fee,
1469 0,
1470 19,
1471 |_epoch_index, _amount| {
1472 epoch_count += 1;
1473 Ok(())
1474 },
1475 20,
1476 )
1477 .expect("should distribute");
1478
1479 assert_eq!(epoch_count, 981);
1481 assert!(leftovers < storage_fee);
1482 }
1483
1484 #[test]
1485 fn should_handle_skip_at_era_boundary() {
1486 let storage_fee = 500000u64;
1488 let start = 0u16;
1489 let skip_until = 20u16; let mut epochs_called = Vec::new();
1492
1493 let _leftovers = refund_storage_fee_to_epochs_map(
1494 storage_fee,
1495 start,
1496 skip_until,
1497 |epoch_index, _amount| {
1498 epochs_called.push(epoch_index);
1499 Ok(())
1500 },
1501 20,
1502 )
1503 .expect("should distribute");
1504
1505 assert_eq!(*epochs_called.first().unwrap(), skip_until);
1507 assert_eq!(epochs_called.len(), 980);
1509 }
1510 }
1511}