dpp/fee/epoch/
distribution.rs

1// MIT LICENSE
2//
3// Copyright (c) 2021 Dash Core Group
4//
5// Permission is hereby granted, free of charge, to any
6// person obtaining a copy of this software and associated
7// documentation files (the "Software"), to deal in the
8// Software without restriction, including without
9// limitation the rights to use, copy, modify, merge,
10// publish, distribute, sublicense, and/or sell copies of
11// the Software, and to permit persons to whom the Software
12// is furnished to do so, subject to the following
13// conditions:
14//
15// The above copyright notice and this permission notice
16// shall be included in all copies or substantial portions
17// of the Software.
18//
19// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
20// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
21// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
22// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
23// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
24// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
26// IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27// DEALINGS IN THE SOFTWARE.
28//
29
30//! Storage fee distribution into epochs
31//!
32//! Data is stored in Platform "forever" currently, which is 50 eras (50 years by default).
33//! To incentivise masternodes to continue store and serve this data,
34//! payments are distributed for entire period split into epochs.
35//! Every epoch, new aggregated storage fees are distributed among epochs
36//! and masternodes receive payouts for previous epoch.
37//!
38
39use 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// TODO: Should be updated from the doc
50
51/// The amount of the perpetual storage fee to be paid out to masternodes per era. Adds up to 1.
52#[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
69/// Distributes storage fees to epochs into `SignedCreditsPerEpoch` and returns leftovers
70pub 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
96/// Distributes refunds to epochs into `SignedCreditsPerEpoch` and returns leftovers
97/// It skips epochs up to specified `skip_until_epoch_index`
98pub 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    // We need to remove the leftovers from the current epoch
126    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
139/// Calculates leftovers and amount of credits by distributing storage fees to epochs
140pub 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) -> Decimal {
169    let paid_epochs = start_repayment_from_epoch_index - start_epoch_index;
170
171    let current_era = (paid_epochs / epochs_per_era) as usize;
172
173    let ratio_used: Decimal =
174        FEE_DISTRIBUTION_TABLE
175            .iter()
176            .enumerate()
177            .filter_map(|(era, epoch_multiplier)| match era.cmp(&current_era) {
178                Ordering::Less => None,
179                Ordering::Equal => {
180                    let amount_epochs_left_in_era = epochs_per_era - paid_epochs % epochs_per_era;
181                    Some(epoch_multiplier.mul(
182                        Decimal::from(amount_epochs_left_in_era) / Decimal::from(epochs_per_era),
183                    ))
184                }
185                Ordering::Greater => Some(*epoch_multiplier),
186            })
187            .sum();
188
189    dec!(1) / ratio_used
190}
191
192/// Let's imagine that we are refunding something from epoch 5
193/// We are at Epoch 12
194/// The refund amount is from Epoch 13 (current + 1) to Epoch 1005 (5 + 1000)
195/// We need to figure out the amount extra those 8 costed
196fn restore_original_removed_credits_amount(
197    refund_amount: Decimal,
198    start_epoch_index: EpochIndex,
199    start_repayment_from_epoch_index: EpochIndex,
200    epochs_per_era: u16,
201) -> Result<Decimal, ProtocolError> {
202    let multiplier = original_removed_credits_multiplier_from(
203        start_epoch_index,
204        start_repayment_from_epoch_index,
205        epochs_per_era,
206    );
207
208    refund_amount
209        .checked_mul(multiplier)
210        .ok_or(ProtocolError::Overflow(
211            "overflow when multiplying with the multiplier (this should be impossible)",
212        ))
213}
214
215/// Distributes storage fees to epochs and call function for each epoch.
216/// Returns leftovers
217fn distribution_storage_fee_to_epochs_map<F>(
218    storage_fee: Credits,
219    start_epoch_index: EpochIndex,
220    mut map_function: F,
221    epochs_per_era: u16,
222) -> Result<DistributionLeftovers, ProtocolError>
223where
224    F: FnMut(EpochIndex, Credits) -> Result<(), ProtocolError>,
225{
226    if storage_fee == 0 {
227        return Ok(0);
228    }
229
230    let storage_fee_dec: Decimal = storage_fee.into();
231
232    let mut distribution_leftover_credits = storage_fee;
233
234    let epochs_per_era_dec = Decimal::from(epochs_per_era);
235
236    for era in 0..PERPETUAL_STORAGE_ERAS {
237        let distribution_for_that_era_ratio = FEE_DISTRIBUTION_TABLE[era as usize];
238
239        let era_fee_share = storage_fee_dec * distribution_for_that_era_ratio;
240
241        let epoch_fee_share_dec = era_fee_share / epochs_per_era_dec;
242
243        let epoch_fee_share: Credits = epoch_fee_share_dec
244            .floor()
245            .to_u64()
246            .ok_or_else(|| ProtocolError::Overflow("storage fees are not fitting in a u64"))?;
247
248        let era_start_epoch_index = start_epoch_index + epochs_per_era * era;
249
250        for epoch_index in era_start_epoch_index..era_start_epoch_index + epochs_per_era {
251            //todo: this can lead to many many calls once we are further along in epochs
252            map_function(epoch_index, epoch_fee_share)?;
253
254            distribution_leftover_credits = distribution_leftover_credits
255                .checked_sub(epoch_fee_share)
256                .ok_or(ProtocolError::Overflow(
257                    "leftovers bigger than initial value",
258                ))?;
259        }
260    }
261
262    Ok(distribution_leftover_credits)
263}
264
265/// Distributes recovered by multiplier original removed
266/// credits to epochs and call function for each epoch.
267/// Leftovers are added to current epoch
268fn refund_storage_fee_to_epochs_map<F>(
269    storage_fee: Credits,
270    start_epoch_index: EpochIndex,
271    skip_until_epoch_index: EpochIndex,
272    mut map_function: F,
273    epochs_per_era: u16,
274) -> Result<DistributionLeftovers, ProtocolError>
275where
276    F: FnMut(EpochIndex, Credits) -> Result<(), ProtocolError>,
277{
278    if storage_fee == 0 {
279        return Ok(0);
280    }
281
282    let storage_fee_dec: Decimal = storage_fee.into();
283
284    let mut distribution_leftover_credits = storage_fee;
285
286    let epochs_per_era_dec = Decimal::from(epochs_per_era);
287
288    let start_era: u16 = (skip_until_epoch_index - start_epoch_index) / epochs_per_era;
289
290    // Let's imagine that we are refunding something from epoch 5
291    // We are at Epoch 12
292    // The refund amount is from Epoch 13 (current + 1) to Epoch 1005 (5 + 1000)
293    // We need to figure out the amount extra those 8 costed
294    let estimated_storage_fee_dec = restore_original_removed_credits_amount(
295        storage_fee_dec,
296        start_epoch_index,
297        skip_until_epoch_index,
298        epochs_per_era,
299    )?;
300
301    for era in start_era..PERPETUAL_STORAGE_ERAS {
302        let distribution_for_that_era_ratio = FEE_DISTRIBUTION_TABLE[era as usize];
303
304        let estimated_era_fee_share = estimated_storage_fee_dec * distribution_for_that_era_ratio;
305
306        let estimated_epoch_fee_share_dec = estimated_era_fee_share / epochs_per_era_dec;
307
308        let estimated_epoch_fee_share: Credits = estimated_epoch_fee_share_dec
309            .floor()
310            .to_u64()
311            .ok_or_else(|| ProtocolError::Overflow("storage fees are not fitting in a u64"))?;
312
313        let era_start_epoch_index = if era == start_era {
314            skip_until_epoch_index
315        } else {
316            start_epoch_index + epochs_per_era * era
317        };
318
319        let era_end_epoch_index = start_epoch_index + ((era + 1) * epochs_per_era);
320
321        for epoch_index in era_start_epoch_index..era_end_epoch_index {
322            map_function(epoch_index, estimated_epoch_fee_share)?;
323
324            distribution_leftover_credits = distribution_leftover_credits
325                .checked_sub(estimated_epoch_fee_share)
326                .ok_or(ProtocolError::Overflow(
327                    "leftovers bigger than initial value",
328                ))?;
329        }
330    }
331    Ok(distribution_leftover_credits)
332}
333
334#[cfg(test)]
335mod tests {
336    use super::*;
337    use crate::fee::epoch::GENESIS_EPOCH_INDEX;
338    use rust_decimal::Decimal;
339    use rust_decimal_macros::dec;
340
341    mod original_removed_credits_multiplier_from {
342        use super::*;
343
344        #[test]
345        fn should_create_multiplier_for_epochs_since_the_beginning() {
346            // the multiplier should be
347            let epoch_0_cost = dec!(0.05000) / dec!(20.0);
348            let multiplier_should_be = dec!(1.0) / (dec!(1.0) - epoch_0_cost);
349
350            let multiplier = original_removed_credits_multiplier_from(0, 1, 20);
351
352            assert_eq!(multiplier_should_be, multiplier);
353        }
354
355        #[test]
356        fn should_create_multiplier_for_epochs_since_24_and_repaid_since_43() {
357            // there were 19 epochs
358            let epoch_0_cost = dec!(19.0) * dec!(0.05000) / dec!(20.0);
359
360            let multiplier_should_be = dec!(1.0) / (dec!(1.0) - epoch_0_cost);
361
362            let multiplier = original_removed_credits_multiplier_from(24, 43, 20);
363
364            assert_eq!(multiplier_should_be, multiplier);
365        }
366    }
367
368    mod fee_distribution_table {
369        use super::*;
370
371        #[test]
372        fn should_have_sum_of_1() {
373            assert_eq!(FEE_DISTRIBUTION_TABLE.iter().sum::<Decimal>(), dec!(1.0),);
374        }
375
376        #[test]
377        fn should_distribute_value() {
378            let value = Decimal::from(i64::MAX);
379
380            let calculated_value: Decimal = FEE_DISTRIBUTION_TABLE
381                .into_iter()
382                .map(|ratio| value * ratio)
383                .sum();
384
385            assert_eq!(calculated_value, value);
386        }
387    }
388
389    mod distribution_storage_fee_to_epochs_map {
390        use super::*;
391
392        #[test]
393        fn should_distribute_nothing_if_storage_fees_are_zero() {
394            let mut calls = 0;
395
396            let leftovers = distribution_storage_fee_to_epochs_map(
397                0,
398                GENESIS_EPOCH_INDEX,
399                |_, _| {
400                    calls += 1;
401
402                    Ok(())
403                },
404                20,
405            )
406            .expect("should distribute storage fee");
407
408            assert_eq!(calls, 0);
409            assert_eq!(leftovers, 0);
410        }
411
412        #[test]
413        fn should_call_function_for_each_epoch_for_50_eras_sequentially() {
414            let mut calls = 0;
415
416            let mut previous_epoch_index = -1;
417
418            let leftovers = distribution_storage_fee_to_epochs_map(
419                100000,
420                GENESIS_EPOCH_INDEX,
421                |epoch_index, _| {
422                    assert_eq!(epoch_index as i32, previous_epoch_index + 1);
423                    previous_epoch_index = epoch_index as i32;
424
425                    calls += 1;
426
427                    Ok(())
428                },
429                20,
430            )
431            .expect("should distribute storage fee");
432
433            assert_eq!(calls, 1000); //20*50
434            assert_eq!(leftovers, 360);
435        }
436    }
437
438    mod distribute_storage_fee_to_epochs_collection {
439        use super::*;
440        use crate::balances::credits::{Creditable, MAX_CREDITS};
441        use crate::fee::SignedCredits;
442
443        #[test]
444        fn should_distribute_max_credits_value_without_overflow() {
445            let storage_fee = MAX_CREDITS;
446
447            let mut credits_per_epochs = SignedCreditsPerEpoch::default();
448
449            let leftovers = distribute_storage_fee_to_epochs_collection(
450                &mut credits_per_epochs,
451                storage_fee,
452                GENESIS_EPOCH_INDEX,
453                20,
454            )
455            .expect("should distribute storage fee");
456
457            // check leftover
458            assert_eq!(leftovers, 507);
459        }
460
461        #[test]
462        fn should_deterministically_distribute_fees() {
463            let storage_fee = 1000000;
464            let current_epoch_index = 42;
465
466            let mut credits_per_epochs = SignedCreditsPerEpoch::default();
467
468            let leftovers = distribute_storage_fee_to_epochs_collection(
469                &mut credits_per_epochs,
470                storage_fee,
471                current_epoch_index,
472                20,
473            )
474            .expect("should distribute storage fee");
475
476            // check leftover
477            assert_eq!(leftovers, 180);
478
479            // compare them with reference table for 20 epochs per era (1000)
480            #[rustfmt::skip]
481                let reference_fees: [SignedCredits; 1000] = [
482                2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500,
483                2500, 2500, 2500, 2500, 2500, 2500, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400,
484                2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2300, 2300,
485                2300, 2300, 2300, 2300, 2300, 2300, 2300, 2300, 2300, 2300, 2300, 2300, 2300, 2300,
486                2300, 2300, 2300, 2300, 2200, 2200, 2200, 2200, 2200, 2200, 2200, 2200, 2200, 2200,
487                2200, 2200, 2200, 2200, 2200, 2200, 2200, 2200, 2200, 2200, 2100, 2100, 2100, 2100,
488                2100, 2100, 2100, 2100, 2100, 2100, 2100, 2100, 2100, 2100, 2100, 2100, 2100, 2100,
489                2100, 2100, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000,
490                2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 1925, 1925, 1925, 1925, 1925, 1925,
491                1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925,
492                1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850,
493                1850, 1850, 1850, 1850, 1850, 1850, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775,
494                1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1700, 1700,
495                1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700,
496                1700, 1700, 1700, 1700, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625,
497                1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1550, 1550, 1550, 1550,
498                1550, 1550, 1550, 1550, 1550, 1550, 1550, 1550, 1550, 1550, 1550, 1550, 1550, 1550,
499                1550, 1550, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475,
500                1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1425, 1425, 1425, 1425, 1425, 1425,
501                1425, 1425, 1425, 1425, 1425, 1425, 1425, 1425, 1425, 1425, 1425, 1425, 1425, 1425,
502                1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375,
503                1375, 1375, 1375, 1375, 1375, 1375, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325,
504                1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1275, 1275,
505                1275, 1275, 1275, 1275, 1275, 1275, 1275, 1275, 1275, 1275, 1275, 1275, 1275, 1275,
506                1275, 1275, 1275, 1275, 1225, 1225, 1225, 1225, 1225, 1225, 1225, 1225, 1225, 1225,
507                1225, 1225, 1225, 1225, 1225, 1225, 1225, 1225, 1225, 1225, 1175, 1175, 1175, 1175,
508                1175, 1175, 1175, 1175, 1175, 1175, 1175, 1175, 1175, 1175, 1175, 1175, 1175, 1175,
509                1175, 1175, 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125,
510                1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1075, 1075, 1075, 1075, 1075, 1075,
511                1075, 1075, 1075, 1075, 1075, 1075, 1075, 1075, 1075, 1075, 1075, 1075, 1075, 1075,
512                1025, 1025, 1025, 1025, 1025, 1025, 1025, 1025, 1025, 1025, 1025, 1025, 1025, 1025,
513                1025, 1025, 1025, 1025, 1025, 1025, 975, 975, 975, 975, 975, 975, 975, 975, 975,
514                975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 937, 937, 937, 937, 937,
515                937, 937, 937, 937, 937, 937, 937, 937, 937, 937, 937, 937, 937, 937, 937, 900,
516                900, 900, 900, 900, 900, 900, 900, 900, 900, 900, 900, 900, 900, 900, 900, 900,
517                900, 900, 900, 862, 862, 862, 862, 862, 862, 862, 862, 862, 862, 862, 862, 862,
518                862, 862, 862, 862, 862, 862, 862, 825, 825, 825, 825, 825, 825, 825, 825, 825,
519                825, 825, 825, 825, 825, 825, 825, 825, 825, 825, 825, 787, 787, 787, 787, 787,
520                787, 787, 787, 787, 787, 787, 787, 787, 787, 787, 787, 787, 787, 787, 787, 750,
521                750, 750, 750, 750, 750, 750, 750, 750, 750, 750, 750, 750, 750, 750, 750, 750,
522                750, 750, 750, 712, 712, 712, 712, 712, 712, 712, 712, 712, 712, 712, 712, 712,
523                712, 712, 712, 712, 712, 712, 712, 675, 675, 675, 675, 675, 675, 675, 675, 675,
524                675, 675, 675, 675, 675, 675, 675, 675, 675, 675, 675, 637, 637, 637, 637, 637,
525                637, 637, 637, 637, 637, 637, 637, 637, 637, 637, 637, 637, 637, 637, 637, 600,
526                600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600,
527                600, 600, 600, 562, 562, 562, 562, 562, 562, 562, 562, 562, 562, 562, 562, 562,
528                562, 562, 562, 562, 562, 562, 562, 525, 525, 525, 525, 525, 525, 525, 525, 525,
529                525, 525, 525, 525, 525, 525, 525, 525, 525, 525, 525, 487, 487, 487, 487, 487,
530                487, 487, 487, 487, 487, 487, 487, 487, 487, 487, 487, 487, 487, 487, 487, 450,
531                450, 450, 450, 450, 450, 450, 450, 450, 450, 450, 450, 450, 450, 450, 450, 450,
532                450, 450, 450, 412, 412, 412, 412, 412, 412, 412, 412, 412, 412, 412, 412, 412,
533                412, 412, 412, 412, 412, 412, 412, 375, 375, 375, 375, 375, 375, 375, 375, 375,
534                375, 375, 375, 375, 375, 375, 375, 375, 375, 375, 375, 337, 337, 337, 337, 337,
535                337, 337, 337, 337, 337, 337, 337, 337, 337, 337, 337, 337, 337, 337, 337, 300,
536                300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300,
537                300, 300, 300, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262,
538                262, 262, 262, 262, 262, 262, 262, 237, 237, 237, 237, 237, 237, 237, 237, 237,
539                237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 212, 212, 212, 212, 212,
540                212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 187,
541                187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187,
542                187, 187, 187, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162,
543                162, 162, 162, 162, 162, 162, 162, 137, 137, 137, 137, 137, 137, 137, 137, 137,
544                137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 112, 112, 112, 112, 112,
545                112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 87,
546                87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 62,
547                62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62
548            ];
549
550            assert_eq!(
551                credits_per_epochs.clone().into_values().collect::<Vec<_>>(),
552                reference_fees
553            );
554
555            let total_distributed: SignedCredits = credits_per_epochs.values().sum();
556
557            assert_eq!(total_distributed.to_unsigned() + leftovers, storage_fee);
558
559            /*
560
561            Repeat distribution to ensure deterministic results
562
563             */
564
565            let leftovers = distribute_storage_fee_to_epochs_collection(
566                &mut credits_per_epochs,
567                storage_fee,
568                current_epoch_index,
569                20,
570            )
571            .expect("should distribute storage fee");
572
573            // assert that all the values doubled meaning that distribution is reproducible
574            assert_eq!(
575                credits_per_epochs.into_values().collect::<Vec<_>>(),
576                reference_fees
577                    .into_iter()
578                    .map(|val| val * 2)
579                    .collect::<Vec<_>>()
580            );
581
582            assert_eq!(leftovers, 180);
583        }
584    }
585
586    mod subtract_refunds_from_epoch_credits_collection {
587        use super::*;
588        use crate::balances::credits::Creditable;
589        use crate::fee::SignedCredits;
590
591        #[test]
592        fn should_deduct_refunds_from_collection_since_specific_epoch_start_at_genesis() {
593            // Example: Bob inserted an element into the tree
594            // He paid slightly more than 1.2 Million credits for this operation that happened at epoch 0.
595            // At epoch 42 we are asking for a refund.
596            // The refund is 1.07 Million credits that were left from the 1.2.
597
598            let start_epoch_index: EpochIndex = GENESIS_EPOCH_INDEX;
599            const REFUNDED_EPOCH_INDEX: EpochIndex = 42;
600            let original_storage_fee = 1200005;
601
602            let (refund_amount, leftovers) = calculate_storage_fee_refund_amount_and_leftovers(
603                original_storage_fee,
604                start_epoch_index,
605                REFUNDED_EPOCH_INDEX,
606                20,
607            )
608            .expect("should distribute storage fee");
609
610            assert_eq!(refund_amount, 1074120);
611            assert_eq!(leftovers, 5);
612
613            let mut credits_per_epochs = SignedCreditsPerEpoch::default();
614
615            subtract_refunds_from_epoch_credits_collection(
616                &mut credits_per_epochs,
617                refund_amount,
618                start_epoch_index,
619                REFUNDED_EPOCH_INDEX,
620                20,
621            )
622            .expect("should distribute storage fee");
623
624            // compare them with reference table
625            // we expect to get 0 for the change of the current epochs balance
626            // this is because there was only 1 refund so leftovers wouldn't have any effect
627            #[rustfmt::skip]
628            let reference_fees: [SignedCredits;
629                (1000 - REFUNDED_EPOCH_INDEX - 1) as usize] = [-2760, -2760, -2760,
630                -2760, -2760, -2760, -2760, -2760, -2760, -2760, -2760, -2760, -2760, -2760, -2760,
631                -2760, -2760, -2640, -2640, -2640, -2640, -2640, -2640, -2640, -2640, -2640, -2640,
632                -2640, -2640, -2640, -2640, -2640, -2640, -2640, -2640, -2640, -2640, -2520, -2520,
633                -2520, -2520, -2520, -2520, -2520, -2520, -2520, -2520, -2520, -2520, -2520, -2520,
634                -2520, -2520, -2520, -2520, -2520, -2520, -2400, -2400, -2400, -2400, -2400, -2400,
635                -2400, -2400, -2400, -2400, -2400, -2400, -2400, -2400, -2400, -2400, -2400, -2400,
636                -2400, -2400, -2310, -2310, -2310, -2310, -2310, -2310, -2310, -2310, -2310, -2310,
637                -2310, -2310, -2310, -2310, -2310, -2310, -2310, -2310, -2310, -2310, -2220, -2220,
638                -2220, -2220, -2220, -2220, -2220, -2220, -2220, -2220, -2220, -2220, -2220, -2220,
639                -2220, -2220, -2220, -2220, -2220, -2220, -2130, -2130, -2130, -2130, -2130, -2130,
640                -2130, -2130, -2130, -2130, -2130, -2130, -2130, -2130, -2130, -2130, -2130, -2130,
641                -2130, -2130, -2040, -2040, -2040, -2040, -2040, -2040, -2040, -2040, -2040, -2040,
642                -2040, -2040, -2040, -2040, -2040, -2040, -2040, -2040, -2040, -2040, -1950, -1950,
643                -1950, -1950, -1950, -1950, -1950, -1950, -1950, -1950, -1950, -1950, -1950, -1950,
644                -1950, -1950, -1950, -1950, -1950, -1950, -1860, -1860, -1860, -1860, -1860, -1860,
645                -1860, -1860, -1860, -1860, -1860, -1860, -1860, -1860, -1860, -1860, -1860, -1860,
646                -1860, -1860, -1770, -1770, -1770, -1770, -1770, -1770, -1770, -1770, -1770, -1770,
647                -1770, -1770, -1770, -1770, -1770, -1770, -1770, -1770, -1770, -1770, -1710, -1710,
648                -1710, -1710, -1710, -1710, -1710, -1710, -1710, -1710, -1710, -1710, -1710, -1710,
649                -1710, -1710, -1710, -1710, -1710, -1710, -1650, -1650, -1650, -1650, -1650, -1650,
650                -1650, -1650, -1650, -1650, -1650, -1650, -1650, -1650, -1650, -1650, -1650, -1650,
651                -1650, -1650, -1590, -1590, -1590, -1590, -1590, -1590, -1590, -1590, -1590, -1590,
652                -1590, -1590, -1590, -1590, -1590, -1590, -1590, -1590, -1590, -1590, -1530, -1530,
653                -1530, -1530, -1530, -1530, -1530, -1530, -1530, -1530, -1530, -1530, -1530, -1530,
654                -1530, -1530, -1530, -1530, -1530, -1530, -1470, -1470, -1470, -1470, -1470, -1470,
655                -1470, -1470, -1470, -1470, -1470, -1470, -1470, -1470, -1470, -1470, -1470, -1470,
656                -1470, -1470, -1410, -1410, -1410, -1410, -1410, -1410, -1410, -1410, -1410, -1410,
657                -1410, -1410, -1410, -1410, -1410, -1410, -1410, -1410, -1410, -1410, -1350, -1350,
658                -1350, -1350, -1350, -1350, -1350, -1350, -1350, -1350, -1350, -1350, -1350, -1350,
659                -1350, -1350, -1350, -1350, -1350, -1350, -1290, -1290, -1290, -1290, -1290, -1290,
660                -1290, -1290, -1290, -1290, -1290, -1290, -1290, -1290, -1290, -1290, -1290, -1290,
661                -1290, -1290, -1230, -1230, -1230, -1230, -1230, -1230, -1230, -1230, -1230, -1230,
662                -1230, -1230, -1230, -1230, -1230, -1230, -1230, -1230, -1230, -1230, -1170, -1170,
663                -1170, -1170, -1170, -1170, -1170, -1170, -1170, -1170, -1170, -1170, -1170, -1170,
664                -1170, -1170, -1170, -1170, -1170, -1170, -1125, -1125, -1125, -1125, -1125, -1125,
665                -1125, -1125, -1125, -1125, -1125, -1125, -1125, -1125, -1125, -1125, -1125, -1125,
666                -1125, -1125, -1080, -1080, -1080, -1080, -1080, -1080, -1080, -1080, -1080, -1080,
667                -1080, -1080, -1080, -1080, -1080, -1080, -1080, -1080, -1080, -1080, -1035, -1035,
668                -1035, -1035, -1035, -1035, -1035, -1035, -1035, -1035, -1035, -1035, -1035, -1035,
669                -1035, -1035, -1035, -1035, -1035, -1035, -990, -990, -990, -990, -990, -990, -990,
670                -990, -990, -990, -990, -990, -990, -990, -990, -990, -990, -990, -990, -990, -945,
671                -945, -945, -945, -945, -945, -945, -945, -945, -945, -945, -945, -945, -945, -945,
672                -945, -945, -945, -945, -945, -900, -900, -900, -900, -900, -900, -900, -900, -900,
673                -900, -900, -900, -900, -900, -900, -900, -900, -900, -900, -900, -855, -855, -855,
674                -855, -855, -855, -855, -855, -855, -855, -855, -855, -855, -855, -855, -855, -855,
675                -855, -855, -855, -810, -810, -810, -810, -810, -810, -810, -810, -810, -810, -810,
676                -810, -810, -810, -810, -810, -810, -810, -810, -810, -765, -765, -765, -765, -765,
677                -765, -765, -765, -765, -765, -765, -765, -765, -765, -765, -765, -765, -765, -765,
678                -765, -720, -720, -720, -720, -720, -720, -720, -720, -720, -720, -720, -720, -720,
679                -720, -720, -720, -720, -720, -720, -720, -675, -675, -675, -675, -675, -675, -675,
680                -675, -675, -675, -675, -675, -675, -675, -675, -675, -675, -675, -675, -675, -630,
681                -630, -630, -630, -630, -630, -630, -630, -630, -630, -630, -630, -630, -630, -630,
682                -630, -630, -630, -630, -630, -585, -585, -585, -585, -585, -585, -585, -585, -585,
683                -585, -585, -585, -585, -585, -585, -585, -585, -585, -585, -585, -540, -540, -540,
684                -540, -540, -540, -540, -540, -540, -540, -540, -540, -540, -540, -540, -540, -540,
685                -540, -540, -540, -495, -495, -495, -495, -495, -495, -495, -495, -495, -495, -495,
686                -495, -495, -495, -495, -495, -495, -495, -495, -495, -450, -450, -450, -450, -450,
687                -450, -450, -450, -450, -450, -450, -450, -450, -450, -450, -450, -450, -450, -450,
688                -450, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405,
689                -405, -405, -405, -405, -405, -405, -405, -360, -360, -360, -360, -360, -360, -360,
690                -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -315,
691                -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315,
692                -315, -315, -315, -315, -315, -285, -285, -285, -285, -285, -285, -285, -285, -285,
693                -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -255, -255, -255,
694                -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255,
695                -255, -255, -255, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225,
696                -225, -225, -225, -225, -225, -225, -225, -225, -225, -195, -195, -195, -195, -195,
697                -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195,
698                -195, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165,
699                -165, -165, -165, -165, -165, -165, -165, -135, -135, -135, -135, -135, -135, -135,
700                -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -105,
701                -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105,
702                -105, -105, -105, -105, -105, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75,
703                -75, -75, -75, -75, -75, -75, -75, -75, -75];
704
705            assert_eq!(
706                credits_per_epochs.clone().into_values().collect::<Vec<_>>(),
707                reference_fees
708            );
709
710            let total_distributed: SignedCredits = credits_per_epochs.values().sum();
711
712            assert_eq!(total_distributed.to_unsigned(), refund_amount);
713        }
714
715        #[test]
716        fn should_deduct_refunds_from_collection_start_epoch_doesnt_matter_check() {
717            for start_epoch_index in 0..150 {
718                let current_epoch_index_where_refund_occurred: EpochIndex = start_epoch_index + 14;
719
720                let original_storage_fee = 3405507;
721                let (refund_amount, leftovers) = calculate_storage_fee_refund_amount_and_leftovers(
722                    original_storage_fee,
723                    start_epoch_index,
724                    current_epoch_index_where_refund_occurred,
725                    20,
726                )
727                .expect("should distribute storage fee");
728
729                assert_eq!(refund_amount, 3277305);
730                assert_eq!(leftovers, 507);
731
732                let multiplier = original_removed_credits_multiplier_from(
733                    start_epoch_index,
734                    current_epoch_index_where_refund_occurred + 1,
735                    20,
736                );
737
738                // it's not going to be completely perfect but it's good enough
739                // there were 24 epochs, on average we would be 12 off
740                // while we could incorporate this offset into the multiplier it would
741                // be overkill for such low credit amounts
742                assert!(
743                    (Decimal::from(refund_amount) * multiplier)
744                        .abs_sub(&Decimal::from(original_storage_fee - leftovers))
745                        < dec!(100)
746                );
747
748                // we do however want to make sure the multiplier makes things smaller
749                assert!(
750                    (Decimal::from(refund_amount) * multiplier)
751                        < Decimal::from(original_storage_fee - leftovers)
752                );
753
754                let mut credits_per_epochs = SignedCreditsPerEpoch::default();
755
756                subtract_refunds_from_epoch_credits_collection(
757                    &mut credits_per_epochs,
758                    refund_amount,
759                    start_epoch_index,
760                    current_epoch_index_where_refund_occurred,
761                    20,
762                )
763                .expect("should distribute storage fee");
764                // compare them with reference table
765                // we expect to get 0 for the change of the current epochs balance
766                // this is because there was only 1 refund so leftovers wouldn't have any effect
767                #[rustfmt::skip]
768                    let reference_fees: Vec<SignedCredits> =
769                    vec![-525, -8512, -8512, -8512, -8512, -8512, -8171, -8171, -8171, -8171, -8171, -8171,
770                        -8171, -8171, -8171, -8171, -8171, -8171, -8171, -8171, -8171, -8171, -8171,
771                        -8171, -8171, -8171, -7831, -7831, -7831, -7831, -7831, -7831, -7831, -7831,
772                        -7831, -7831, -7831, -7831, -7831, -7831, -7831, -7831, -7831, -7831, -7831,
773                        -7831, -7490, -7490, -7490, -7490, -7490, -7490, -7490, -7490, -7490, -7490,
774                        -7490, -7490, -7490, -7490, -7490, -7490, -7490, -7490, -7490, -7490, -7150,
775                        -7150, -7150, -7150, -7150, -7150, -7150, -7150, -7150, -7150, -7150, -7150,
776                        -7150, -7150, -7150, -7150, -7150, -7150, -7150, -7150, -6809, -6809, -6809,
777                        -6809, -6809, -6809, -6809, -6809, -6809, -6809, -6809, -6809, -6809, -6809,
778                        -6809, -6809, -6809, -6809, -6809, -6809, -6554, -6554, -6554, -6554, -6554,
779                        -6554, -6554, -6554, -6554, -6554, -6554, -6554, -6554, -6554, -6554, -6554,
780                        -6554, -6554, -6554, -6554, -6299, -6299, -6299, -6299, -6299, -6299, -6299,
781                        -6299, -6299, -6299, -6299, -6299, -6299, -6299, -6299, -6299, -6299, -6299,
782                        -6299, -6299, -6043, -6043, -6043, -6043, -6043, -6043, -6043, -6043, -6043,
783                        -6043, -6043, -6043, -6043, -6043, -6043, -6043, -6043, -6043, -6043, -6043,
784                        -5788, -5788, -5788, -5788, -5788, -5788, -5788, -5788, -5788, -5788, -5788,
785                        -5788, -5788, -5788, -5788, -5788, -5788, -5788, -5788, -5788, -5533, -5533,
786                        -5533, -5533, -5533, -5533, -5533, -5533, -5533, -5533, -5533, -5533, -5533,
787                        -5533, -5533, -5533, -5533, -5533, -5533, -5533, -5277, -5277, -5277, -5277,
788                        -5277, -5277, -5277, -5277, -5277, -5277, -5277, -5277, -5277, -5277, -5277,
789                        -5277, -5277, -5277, -5277, -5277, -5022, -5022, -5022, -5022, -5022, -5022,
790                        -5022, -5022, -5022, -5022, -5022, -5022, -5022, -5022, -5022, -5022, -5022,
791                        -5022, -5022, -5022, -4852, -4852, -4852, -4852, -4852, -4852, -4852, -4852,
792                        -4852, -4852, -4852, -4852, -4852, -4852, -4852, -4852, -4852, -4852, -4852,
793                        -4852, -4681, -4681, -4681, -4681, -4681, -4681, -4681, -4681, -4681, -4681,
794                        -4681, -4681, -4681, -4681, -4681, -4681, -4681, -4681, -4681, -4681, -4511,
795                        -4511, -4511, -4511, -4511, -4511, -4511, -4511, -4511, -4511, -4511, -4511,
796                        -4511, -4511, -4511, -4511, -4511, -4511, -4511, -4511, -4341, -4341, -4341,
797                        -4341, -4341, -4341, -4341, -4341, -4341, -4341, -4341, -4341, -4341, -4341,
798                        -4341, -4341, -4341, -4341, -4341, -4341, -4171, -4171, -4171, -4171, -4171,
799                        -4171, -4171, -4171, -4171, -4171, -4171, -4171, -4171, -4171, -4171, -4171,
800                        -4171, -4171, -4171, -4171, -4000, -4000, -4000, -4000, -4000, -4000, -4000,
801                        -4000, -4000, -4000, -4000, -4000, -4000, -4000, -4000, -4000, -4000, -4000,
802                        -4000, -4000, -3830, -3830, -3830, -3830, -3830, -3830, -3830, -3830, -3830,
803                        -3830, -3830, -3830, -3830, -3830, -3830, -3830, -3830, -3830, -3830, -3830,
804                        -3660, -3660, -3660, -3660, -3660, -3660, -3660, -3660, -3660, -3660, -3660,
805                        -3660, -3660, -3660, -3660, -3660, -3660, -3660, -3660, -3660, -3490, -3490,
806                        -3490, -3490, -3490, -3490, -3490, -3490, -3490, -3490, -3490, -3490, -3490,
807                        -3490, -3490, -3490, -3490, -3490, -3490, -3490, -3319, -3319, -3319, -3319,
808                        -3319, -3319, -3319, -3319, -3319, -3319, -3319, -3319, -3319, -3319, -3319,
809                        -3319, -3319, -3319, -3319, -3319, -3192, -3192, -3192, -3192, -3192, -3192,
810                        -3192, -3192, -3192, -3192, -3192, -3192, -3192, -3192, -3192, -3192, -3192,
811                        -3192, -3192, -3192, -3064, -3064, -3064, -3064, -3064, -3064, -3064, -3064,
812                        -3064, -3064, -3064, -3064, -3064, -3064, -3064, -3064, -3064, -3064, -3064,
813                        -3064, -2936, -2936, -2936, -2936, -2936, -2936, -2936, -2936, -2936, -2936,
814                        -2936, -2936, -2936, -2936, -2936, -2936, -2936, -2936, -2936, -2936, -2809,
815                        -2809, -2809, -2809, -2809, -2809, -2809, -2809, -2809, -2809, -2809, -2809,
816                        -2809, -2809, -2809, -2809, -2809, -2809, -2809, -2809, -2681, -2681, -2681,
817                        -2681, -2681, -2681, -2681, -2681, -2681, -2681, -2681, -2681, -2681, -2681,
818                        -2681, -2681, -2681, -2681, -2681, -2681, -2553, -2553, -2553, -2553, -2553,
819                        -2553, -2553, -2553, -2553, -2553, -2553, -2553, -2553, -2553, -2553, -2553,
820                        -2553, -2553, -2553, -2553, -2426, -2426, -2426, -2426, -2426, -2426, -2426,
821                        -2426, -2426, -2426, -2426, -2426, -2426, -2426, -2426, -2426, -2426, -2426,
822                        -2426, -2426, -2298, -2298, -2298, -2298, -2298, -2298, -2298, -2298, -2298,
823                        -2298, -2298, -2298, -2298, -2298, -2298, -2298, -2298, -2298, -2298, -2298,
824                        -2170, -2170, -2170, -2170, -2170, -2170, -2170, -2170, -2170, -2170, -2170,
825                        -2170, -2170, -2170, -2170, -2170, -2170, -2170, -2170, -2170, -2042, -2042,
826                        -2042, -2042, -2042, -2042, -2042, -2042, -2042, -2042, -2042, -2042, -2042,
827                        -2042, -2042, -2042, -2042, -2042, -2042, -2042, -1915, -1915, -1915, -1915,
828                        -1915, -1915, -1915, -1915, -1915, -1915, -1915, -1915, -1915, -1915, -1915,
829                        -1915, -1915, -1915, -1915, -1915, -1787, -1787, -1787, -1787, -1787, -1787,
830                        -1787, -1787, -1787, -1787, -1787, -1787, -1787, -1787, -1787, -1787, -1787,
831                        -1787, -1787, -1787, -1659, -1659, -1659, -1659, -1659, -1659, -1659, -1659,
832                        -1659, -1659, -1659, -1659, -1659, -1659, -1659, -1659, -1659, -1659, -1659,
833                        -1659, -1532, -1532, -1532, -1532, -1532, -1532, -1532, -1532, -1532, -1532,
834                        -1532, -1532, -1532, -1532, -1532, -1532, -1532, -1532, -1532, -1532, -1404,
835                        -1404, -1404, -1404, -1404, -1404, -1404, -1404, -1404, -1404, -1404, -1404,
836                        -1404, -1404, -1404, -1404, -1404, -1404, -1404, -1404, -1276, -1276, -1276,
837                        -1276, -1276, -1276, -1276, -1276, -1276, -1276, -1276, -1276, -1276, -1276,
838                        -1276, -1276, -1276, -1276, -1276, -1276, -1149, -1149, -1149, -1149, -1149,
839                        -1149, -1149, -1149, -1149, -1149, -1149, -1149, -1149, -1149, -1149, -1149,
840                        -1149, -1149, -1149, -1149, -1021, -1021, -1021, -1021, -1021, -1021, -1021,
841                        -1021, -1021, -1021, -1021, -1021, -1021, -1021, -1021, -1021, -1021, -1021,
842                        -1021, -1021, -893, -893, -893, -893, -893, -893, -893, -893, -893, -893,
843                        -893, -893, -893, -893, -893, -893, -893, -893, -893, -893, -808, -808, -808,
844                        -808, -808, -808, -808, -808, -808, -808, -808, -808, -808, -808, -808, -808,
845                        -808, -808, -808, -808, -723, -723, -723, -723, -723, -723, -723, -723, -723,
846                        -723, -723, -723, -723, -723, -723, -723, -723, -723, -723, -723, -638, -638,
847                        -638, -638, -638, -638, -638, -638, -638, -638, -638, -638, -638, -638, -638,
848                        -638, -638, -638, -638, -638, -553, -553, -553, -553, -553, -553, -553, -553,
849                        -553, -553, -553, -553, -553, -553, -553, -553, -553, -553, -553, -553, -468,
850                        -468, -468, -468, -468, -468, -468, -468, -468, -468, -468, -468, -468, -468,
851                        -468, -468, -468, -468, -468, -468, -383, -383, -383, -383, -383, -383, -383,
852                        -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383,
853                        -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297,
854                        -297, -297, -297, -297, -297, -297, -297, -212, -212, -212, -212, -212, -212,
855                        -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212,
856                        -212];
857
858                assert_eq!(
859                    credits_per_epochs.clone().into_values().collect::<Vec<_>>(),
860                    reference_fees
861                );
862
863                let total_distributed: SignedCredits = credits_per_epochs.values().sum();
864
865                assert_eq!(total_distributed.to_unsigned(), refund_amount);
866            }
867        }
868
869        #[test]
870        fn should_deduct_refunds_from_two_collection_since_specific_epoch() {
871            const CURRENT_EPOCH_INDEX_WHERE_REFUND_OCCURRED: EpochIndex = 42;
872            let mut credits_per_epochs = SignedCreditsPerEpoch::default();
873
874            // First_refund
875
876            // Example: Bob inserted an element into the tree
877            // He paid slightly more than 1.2 Million credits for this operation that happened at epoch 0.
878            // At epoch 42 we are asking for a refund.
879            // The refund is 1.07 Million credits that were left from the 1.2.
880
881            let first_start_epoch_index: EpochIndex = GENESIS_EPOCH_INDEX;
882
883            let first_original_storage_fee = 1200005;
884            let (first_refund_amount, leftovers) =
885                calculate_storage_fee_refund_amount_and_leftovers(
886                    first_original_storage_fee,
887                    first_start_epoch_index,
888                    CURRENT_EPOCH_INDEX_WHERE_REFUND_OCCURRED,
889                    20,
890                )
891                .expect("should distribute storage fee");
892
893            assert_eq!(first_refund_amount, 1074120);
894            assert_eq!(leftovers, 5);
895
896            subtract_refunds_from_epoch_credits_collection(
897                &mut credits_per_epochs,
898                first_refund_amount,
899                first_start_epoch_index,
900                CURRENT_EPOCH_INDEX_WHERE_REFUND_OCCURRED,
901                20,
902            )
903            .expect("should distribute storage fee");
904
905            // Second_refund
906
907            // Example: Bob inserted an element into the tree
908            // He paid slightly more than 3.4 Million credits for this operation that happened at epoch 0.
909            // At epoch 42 we are asking for a refund.
910
911            const SECOND_START_EPOCH_INDEX: EpochIndex = 28;
912
913            let second_original_storage_fee = 3405507;
914            let (second_refund_amount, leftovers) =
915                calculate_storage_fee_refund_amount_and_leftovers(
916                    second_original_storage_fee,
917                    SECOND_START_EPOCH_INDEX,
918                    CURRENT_EPOCH_INDEX_WHERE_REFUND_OCCURRED,
919                    20,
920                )
921                .expect("should distribute storage fee");
922
923            assert_eq!(second_refund_amount, 3277305);
924            assert_eq!(leftovers, 507);
925
926            let multiplier = original_removed_credits_multiplier_from(
927                SECOND_START_EPOCH_INDEX,
928                CURRENT_EPOCH_INDEX_WHERE_REFUND_OCCURRED + 1,
929                20,
930            );
931
932            // it's not going to be completely perfect but it's good enough
933            // there were 24 epochs, on average we would be 12 off
934            // while we could incorporate this offset into the multiplier it would
935            // be overkill for such low credit amounts
936            assert!(
937                (Decimal::from(second_refund_amount) * multiplier)
938                    .abs_sub(&Decimal::from(second_original_storage_fee - leftovers))
939                    < dec!(100)
940            );
941
942            // we do however want to make sure the multiplier makes things smaller
943            assert!(
944                (Decimal::from(second_refund_amount) * multiplier)
945                    < Decimal::from(second_original_storage_fee - leftovers)
946            );
947
948            subtract_refunds_from_epoch_credits_collection(
949                &mut credits_per_epochs,
950                second_refund_amount,
951                SECOND_START_EPOCH_INDEX,
952                CURRENT_EPOCH_INDEX_WHERE_REFUND_OCCURRED,
953                20,
954            )
955            .expect("should distribute storage fee");
956            // compare them with reference table
957            // we expect to get 0 for the change of the current epochs balance
958            // this is because there was only 1 refund so leftovers wouldn't have any effect
959            #[rustfmt::skip]
960                let reference_fees: [SignedCredits;
961                (SECOND_START_EPOCH_INDEX + 1000 - CURRENT_EPOCH_INDEX_WHERE_REFUND_OCCURRED) as usize] =
962                [-525, -11272, -11272, -11272, -11272, -11272, -10931, -10931, -10931, -10931,
963                    -10931, -10931, -10931, -10931, -10931, -10931, -10931, -10931, -10811, -10811,
964                    -10811, -10811, -10811, -10811, -10811, -10811, -10471, -10471, -10471, -10471,
965                    -10471, -10471, -10471, -10471, -10471, -10471, -10471, -10471, -10351, -10351,
966                    -10351, -10351, -10351, -10351, -10351, -10351, -10010, -10010, -10010, -10010,
967                    -10010, -10010, -10010, -10010, -10010, -10010, -10010, -10010, -9890, -9890,
968                    -9890, -9890, -9890, -9890, -9890, -9890, -9550, -9550, -9550, -9550, -9550,
969                    -9550, -9550, -9550, -9550, -9550, -9550, -9550, -9460, -9460, -9460, -9460,
970                    -9460, -9460, -9460, -9460, -9119, -9119, -9119, -9119, -9119, -9119, -9119,
971                    -9119, -9119, -9119, -9119, -9119, -9029, -9029, -9029, -9029, -9029, -9029,
972                    -9029, -9029, -8774, -8774, -8774, -8774, -8774, -8774, -8774, -8774, -8774,
973                    -8774, -8774, -8774, -8684, -8684, -8684, -8684, -8684, -8684, -8684, -8684,
974                    -8429, -8429, -8429, -8429, -8429, -8429, -8429, -8429, -8429, -8429, -8429,
975                    -8429, -8339, -8339, -8339, -8339, -8339, -8339, -8339, -8339, -8083, -8083,
976                    -8083, -8083, -8083, -8083, -8083, -8083, -8083, -8083, -8083, -8083, -7993,
977                    -7993, -7993, -7993, -7993, -7993, -7993, -7993, -7738, -7738, -7738, -7738,
978                    -7738, -7738, -7738, -7738, -7738, -7738, -7738, -7738, -7648, -7648, -7648,
979                    -7648, -7648, -7648, -7648, -7648, -7393, -7393, -7393, -7393, -7393, -7393,
980                    -7393, -7393, -7393, -7393, -7393, -7393, -7303, -7303, -7303, -7303, -7303,
981                    -7303, -7303, -7303, -7047, -7047, -7047, -7047, -7047, -7047, -7047, -7047,
982                    -7047, -7047, -7047, -7047, -6987, -6987, -6987, -6987, -6987, -6987, -6987,
983                    -6987, -6732, -6732, -6732, -6732, -6732, -6732, -6732, -6732, -6732, -6732,
984                    -6732, -6732, -6672, -6672, -6672, -6672, -6672, -6672, -6672, -6672, -6502,
985                    -6502, -6502, -6502, -6502, -6502, -6502, -6502, -6502, -6502, -6502, -6502,
986                    -6442, -6442, -6442, -6442, -6442, -6442, -6442, -6442, -6271, -6271, -6271,
987                    -6271, -6271, -6271, -6271, -6271, -6271, -6271, -6271, -6271, -6211, -6211,
988                    -6211, -6211, -6211, -6211, -6211, -6211, -6041, -6041, -6041, -6041, -6041,
989                    -6041, -6041, -6041, -6041, -6041, -6041, -6041, -5981, -5981, -5981, -5981,
990                    -5981, -5981, -5981, -5981, -5811, -5811, -5811, -5811, -5811, -5811, -5811,
991                    -5811, -5811, -5811, -5811, -5811, -5751, -5751, -5751, -5751, -5751, -5751,
992                    -5751, -5751, -5581, -5581, -5581, -5581, -5581, -5581, -5581, -5581, -5581,
993                    -5581, -5581, -5581, -5521, -5521, -5521, -5521, -5521, -5521, -5521, -5521,
994                    -5350, -5350, -5350, -5350, -5350, -5350, -5350, -5350, -5350, -5350, -5350,
995                    -5350, -5290, -5290, -5290, -5290, -5290, -5290, -5290, -5290, -5120, -5120,
996                    -5120, -5120, -5120, -5120, -5120, -5120, -5120, -5120, -5120, -5120, -5060,
997                    -5060, -5060, -5060, -5060, -5060, -5060, -5060, -4890, -4890, -4890, -4890,
998                    -4890, -4890, -4890, -4890, -4890, -4890, -4890, -4890, -4830, -4830, -4830,
999                    -4830, -4830, -4830, -4830, -4830, -4660, -4660, -4660, -4660, -4660, -4660,
1000                    -4660, -4660, -4660, -4660, -4660, -4660, -4615, -4615, -4615, -4615, -4615,
1001                    -4615, -4615, -4615, -4444, -4444, -4444, -4444, -4444, -4444, -4444, -4444,
1002                    -4444, -4444, -4444, -4444, -4399, -4399, -4399, -4399, -4399, -4399, -4399,
1003                    -4399, -4272, -4272, -4272, -4272, -4272, -4272, -4272, -4272, -4272, -4272,
1004                    -4272, -4272, -4227, -4227, -4227, -4227, -4227, -4227, -4227, -4227, -4099,
1005                    -4099, -4099, -4099, -4099, -4099, -4099, -4099, -4099, -4099, -4099, -4099,
1006                    -4054, -4054, -4054, -4054, -4054, -4054, -4054, -4054, -3926, -3926, -3926,
1007                    -3926, -3926, -3926, -3926, -3926, -3926, -3926, -3926, -3926, -3881, -3881,
1008                    -3881, -3881, -3881, -3881, -3881, -3881, -3754, -3754, -3754, -3754, -3754,
1009                    -3754, -3754, -3754, -3754, -3754, -3754, -3754, -3709, -3709, -3709, -3709,
1010                    -3709, -3709, -3709, -3709, -3581, -3581, -3581, -3581, -3581, -3581, -3581,
1011                    -3581, -3581, -3581, -3581, -3581, -3536, -3536, -3536, -3536, -3536, -3536,
1012                    -3536, -3536, -3408, -3408, -3408, -3408, -3408, -3408, -3408, -3408, -3408,
1013                    -3408, -3408, -3408, -3363, -3363, -3363, -3363, -3363, -3363, -3363, -3363,
1014                    -3236, -3236, -3236, -3236, -3236, -3236, -3236, -3236, -3236, -3236, -3236,
1015                    -3236, -3191, -3191, -3191, -3191, -3191, -3191, -3191, -3191, -3063, -3063,
1016                    -3063, -3063, -3063, -3063, -3063, -3063, -3063, -3063, -3063, -3063, -3018,
1017                    -3018, -3018, -3018, -3018, -3018, -3018, -3018, -2890, -2890, -2890, -2890,
1018                    -2890, -2890, -2890, -2890, -2890, -2890, -2890, -2890, -2845, -2845, -2845,
1019                    -2845, -2845, -2845, -2845, -2845, -2717, -2717, -2717, -2717, -2717, -2717,
1020                    -2717, -2717, -2717, -2717, -2717, -2717, -2672, -2672, -2672, -2672, -2672,
1021                    -2672, -2672, -2672, -2545, -2545, -2545, -2545, -2545, -2545, -2545, -2545,
1022                    -2545, -2545, -2545, -2545, -2500, -2500, -2500, -2500, -2500, -2500, -2500,
1023                    -2500, -2372, -2372, -2372, -2372, -2372, -2372, -2372, -2372, -2372, -2372,
1024                    -2372, -2372, -2327, -2327, -2327, -2327, -2327, -2327, -2327, -2327, -2199,
1025                    -2199, -2199, -2199, -2199, -2199, -2199, -2199, -2199, -2199, -2199, -2199,
1026                    -2154, -2154, -2154, -2154, -2154, -2154, -2154, -2154, -2027, -2027, -2027,
1027                    -2027, -2027, -2027, -2027, -2027, -2027, -2027, -2027, -2027, -1982, -1982,
1028                    -1982, -1982, -1982, -1982, -1982, -1982, -1854, -1854, -1854, -1854, -1854,
1029                    -1854, -1854, -1854, -1854, -1854, -1854, -1854, -1809, -1809, -1809, -1809,
1030                    -1809, -1809, -1809, -1809, -1681, -1681, -1681, -1681, -1681, -1681, -1681,
1031                    -1681, -1681, -1681, -1681, -1681, -1636, -1636, -1636, -1636, -1636, -1636,
1032                    -1636, -1636, -1509, -1509, -1509, -1509, -1509, -1509, -1509, -1509, -1509,
1033                    -1509, -1509, -1509, -1464, -1464, -1464, -1464, -1464, -1464, -1464, -1464,
1034                    -1336, -1336, -1336, -1336, -1336, -1336, -1336, -1336, -1336, -1336, -1336,
1035                    -1336, -1306, -1306, -1306, -1306, -1306, -1306, -1306, -1306, -1178, -1178,
1036                    -1178, -1178, -1178, -1178, -1178, -1178, -1178, -1178, -1178, -1178, -1148,
1037                    -1148, -1148, -1148, -1148, -1148, -1148, -1148, -1063, -1063, -1063, -1063,
1038                    -1063, -1063, -1063, -1063, -1063, -1063, -1063, -1063, -1033, -1033, -1033,
1039                    -1033, -1033, -1033, -1033, -1033, -948, -948, -948, -948, -948, -948, -948,
1040                    -948, -948, -948, -948, -948, -918, -918, -918, -918, -918, -918, -918, -918,
1041                    -833, -833, -833, -833, -833, -833, -833, -833, -833, -833, -833, -833, -803,
1042                    -803, -803, -803, -803, -803, -803, -803, -718, -718, -718, -718, -718, -718,
1043                    -718, -718, -718, -718, -718, -718, -688, -688, -688, -688, -688, -688, -688,
1044                    -688, -603, -603, -603, -603, -603, -603, -603, -603, -603, -603, -603, -603,
1045                    -573, -573, -573, -573, -573, -573, -573, -573, -488, -488, -488, -488, -488,
1046                    -488, -488, -488, -488, -488, -488, -488, -458, -458, -458, -458, -458, -458,
1047                    -458, -458, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372,
1048                    -372, -297, -297, -297, -297, -297, -297, -297, -297, -212, -212, -212, -212,
1049                    -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212,
1050                    -212, -212, -212];
1051
1052            assert_eq!(
1053                credits_per_epochs.clone().into_values().collect::<Vec<_>>(),
1054                reference_fees
1055            );
1056
1057            let total_distributed: SignedCredits = credits_per_epochs.values().sum();
1058
1059            assert_eq!(
1060                total_distributed.to_unsigned(),
1061                first_refund_amount + second_refund_amount
1062            );
1063        }
1064    }
1065
1066    mod calculate_storage_fee_refund_amount_and_leftovers {
1067        use super::*;
1068
1069        #[test]
1070        fn should_calculate_amount_and_leftovers() {
1071            let storage_fee = 10000;
1072
1073            let (amount, leftovers) = calculate_storage_fee_refund_amount_and_leftovers(
1074                storage_fee,
1075                GENESIS_EPOCH_INDEX + 1,
1076                2,
1077                20,
1078            )
1079            .expect("should distribute storage fee");
1080
1081            let first_two_epochs_amount = 50;
1082
1083            assert_eq!(leftovers, 400);
1084            assert_eq!(amount, storage_fee - leftovers - first_two_epochs_amount);
1085        }
1086
1087        #[test]
1088        fn should_return_zero_amount_and_zero_leftovers_for_zero_storage_fee() {
1089            let (amount, leftovers) =
1090                calculate_storage_fee_refund_amount_and_leftovers(0, GENESIS_EPOCH_INDEX, 10, 20)
1091                    .expect("should handle zero storage fee");
1092
1093            assert_eq!(amount, 0);
1094            assert_eq!(leftovers, 0);
1095        }
1096
1097        #[test]
1098        fn should_return_zero_refund_when_start_epoch_equals_current_epoch() {
1099            // When start == current, skipped_amount covers epoch 0 only (the one epoch
1100            // between start_epoch_index and current_epoch_index + 1 = 1).
1101            let storage_fee = 1000000;
1102            let epoch = 0;
1103
1104            let (amount, leftovers) =
1105                calculate_storage_fee_refund_amount_and_leftovers(storage_fee, epoch, epoch, 20)
1106                    .expect("should distribute storage fee");
1107
1108            // Only epoch 0 is skipped (cost = floor(1000000 * 0.05 / 20) = 2500).
1109            // The refund amount is everything except the skipped epoch and leftovers.
1110            assert_eq!(amount, storage_fee - 2500 - leftovers);
1111        }
1112
1113        #[test]
1114        fn should_calculate_correctly_with_non_genesis_start() {
1115            let storage_fee = 500000;
1116            let start = 100;
1117            let current = 110;
1118
1119            let (amount, leftovers) =
1120                calculate_storage_fee_refund_amount_and_leftovers(storage_fee, start, current, 20)
1121                    .expect("should distribute storage fee");
1122
1123            // Verify invariant: amount + skipped + leftovers = storage_fee
1124            assert_eq!(
1125                amount + (storage_fee - amount - leftovers) + leftovers,
1126                storage_fee
1127            );
1128            // Amount must be less than total
1129            assert!(amount < storage_fee);
1130            assert!(leftovers < storage_fee);
1131        }
1132
1133        #[test]
1134        fn should_handle_large_epoch_gap() {
1135            // current_epoch far from start
1136            let storage_fee = 10_000_000;
1137            let start = 0;
1138            let current = 500; // halfway through the 1000 total epochs
1139
1140            let (amount, leftovers) =
1141                calculate_storage_fee_refund_amount_and_leftovers(storage_fee, start, current, 20)
1142                    .expect("should handle large epoch gap");
1143
1144            // Refund amount should be smaller because most epochs have been paid out
1145            assert!(amount < storage_fee / 2);
1146            assert!(leftovers < storage_fee);
1147        }
1148    }
1149
1150    mod additional_original_removed_credits_multiplier_from {
1151        use super::*;
1152
1153        #[test]
1154        fn should_create_multiplier_of_one_when_no_epochs_have_passed() {
1155            // When start_repayment == start, paid_epochs = 0, ratio_used = full table sum = 1.0
1156            // So multiplier = 1/1 = 1
1157            let multiplier = original_removed_credits_multiplier_from(0, 0, 20);
1158            assert_eq!(multiplier, dec!(1));
1159        }
1160
1161        #[test]
1162        fn should_increase_multiplier_as_more_epochs_pass() {
1163            let m1 = original_removed_credits_multiplier_from(0, 5, 20);
1164            let m2 = original_removed_credits_multiplier_from(0, 10, 20);
1165            let m3 = original_removed_credits_multiplier_from(0, 19, 20);
1166
1167            // More paid epochs means less ratio remaining, so multiplier increases
1168            assert!(m1 < m2);
1169            assert!(m2 < m3);
1170        }
1171
1172        #[test]
1173        fn should_handle_era_boundary_crossing() {
1174            // paid_epochs = 20 means we enter the second era exactly
1175            let m_at_boundary = original_removed_credits_multiplier_from(0, 20, 20);
1176            let m_before_boundary = original_removed_credits_multiplier_from(0, 19, 20);
1177            let m_after_boundary = original_removed_credits_multiplier_from(0, 21, 20);
1178
1179            // At the boundary, the entire first era (0.05) is consumed
1180            assert!(m_at_boundary > m_before_boundary);
1181            assert!(m_after_boundary > m_at_boundary);
1182        }
1183
1184        #[test]
1185        fn should_handle_different_epochs_per_era() {
1186            // With 40 epochs per era (the default), 40 paid epochs = 1 full era
1187            let m_40 = original_removed_credits_multiplier_from(0, 40, 40);
1188            // With 20 epochs per era, 20 paid epochs = 1 full era
1189            let m_20 = original_removed_credits_multiplier_from(0, 20, 20);
1190
1191            // Both consume exactly one full era of 0.05, so multipliers should be equal
1192            assert_eq!(m_40, m_20);
1193        }
1194
1195        #[test]
1196        fn should_produce_same_multiplier_regardless_of_absolute_epoch_offset() {
1197            // The multiplier depends only on the difference, not absolute indices
1198            let m1 = original_removed_credits_multiplier_from(0, 15, 20);
1199            let m2 = original_removed_credits_multiplier_from(100, 115, 20);
1200            let m3 = original_removed_credits_multiplier_from(5000, 5015, 20);
1201
1202            assert_eq!(m1, m2);
1203            assert_eq!(m2, m3);
1204        }
1205    }
1206
1207    mod additional_restore_original_removed_credits_amount {
1208        use super::*;
1209
1210        #[test]
1211        fn should_restore_to_original_when_no_epochs_passed() {
1212            // If start_repayment == start, multiplier is 1.0, so restored == refund_amount
1213            let refund = dec!(1000000);
1214            let restored = restore_original_removed_credits_amount(refund, 0, 0, 20)
1215                .expect("should not overflow");
1216            assert_eq!(restored, refund);
1217        }
1218
1219        #[test]
1220        fn should_increase_amount_when_epochs_have_passed() {
1221            // After some epochs, the multiplier > 1, so restored > refund
1222            let refund = dec!(500000);
1223            let restored = restore_original_removed_credits_amount(refund, 0, 10, 20)
1224                .expect("should not overflow");
1225            assert!(restored > refund);
1226        }
1227
1228        #[test]
1229        fn should_handle_zero_refund_amount() {
1230            let restored = restore_original_removed_credits_amount(dec!(0), 0, 10, 20)
1231                .expect("should handle zero");
1232            assert_eq!(restored, dec!(0));
1233        }
1234    }
1235
1236    mod additional_refund_storage_fee_to_epochs_map {
1237        use super::*;
1238
1239        #[test]
1240        fn should_return_zero_leftovers_for_zero_storage_fee() {
1241            let leftovers = refund_storage_fee_to_epochs_map(0, 0, 1, |_, _| Ok(()), 20)
1242                .expect("should handle zero");
1243            assert_eq!(leftovers, 0);
1244        }
1245
1246        #[test]
1247        fn should_skip_epochs_before_skip_until_index() {
1248            let storage_fee = 1000000u64;
1249            let start = 0u16;
1250            let skip_until = 10u16;
1251
1252            let mut min_epoch_seen = u16::MAX;
1253
1254            let _leftovers = refund_storage_fee_to_epochs_map(
1255                storage_fee,
1256                start,
1257                skip_until,
1258                |epoch_index, _amount| {
1259                    if epoch_index < min_epoch_seen {
1260                        min_epoch_seen = epoch_index;
1261                    }
1262                    Ok(())
1263                },
1264                20,
1265            )
1266            .expect("should distribute refund");
1267
1268            // The first epoch called should be >= skip_until
1269            assert!(min_epoch_seen >= skip_until);
1270        }
1271
1272        #[test]
1273        fn should_distribute_to_single_remaining_epoch_in_era() {
1274            // skip_until is 19 (last epoch of era 0), start is 0
1275            // This means only 1 epoch remains in era 0
1276            let storage_fee = 100000u64;
1277
1278            let mut epoch_count = 0u32;
1279
1280            let leftovers = refund_storage_fee_to_epochs_map(
1281                storage_fee,
1282                0,
1283                19,
1284                |_epoch_index, _amount| {
1285                    epoch_count += 1;
1286                    Ok(())
1287                },
1288                20,
1289            )
1290            .expect("should distribute");
1291
1292            // Total epochs = (1000 - 19) = 981 epochs should be called
1293            assert_eq!(epoch_count, 981);
1294            assert!(leftovers < storage_fee);
1295        }
1296
1297        #[test]
1298        fn should_handle_skip_at_era_boundary() {
1299            // skip_until exactly at era 1 start
1300            let storage_fee = 500000u64;
1301            let start = 0u16;
1302            let skip_until = 20u16; // era 1 starts here
1303
1304            let mut epochs_called = Vec::new();
1305
1306            let _leftovers = refund_storage_fee_to_epochs_map(
1307                storage_fee,
1308                start,
1309                skip_until,
1310                |epoch_index, _amount| {
1311                    epochs_called.push(epoch_index);
1312                    Ok(())
1313                },
1314                20,
1315            )
1316            .expect("should distribute");
1317
1318            // First epoch called should be exactly skip_until
1319            assert_eq!(*epochs_called.first().unwrap(), skip_until);
1320            // Total = 1000 - 20 = 980
1321            assert_eq!(epochs_called.len(), 980);
1322        }
1323    }
1324}