1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
// This file is part of Astar.

// Copyright (C) Stake Technologies Pte.Ltd.
// SPDX-License-Identifier: GPL-3.0-or-later

// Astar is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// Astar is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with Astar. If not, see <http://www.gnu.org/licenses/>.

#![cfg_attr(not(feature = "std"), no_std)]

use frame_support::{
    migrations::{MigrationId, SteppedMigration, SteppedMigrationError},
    weights::WeightMeter,
};
use pallet_vesting::{Vesting, VestingInfo};
use sp_arithmetic::traits::{SaturatedConversion, Saturating};
use sp_runtime::{traits::BlockNumberProvider, Percent};

pub use pallet::*;

#[cfg(feature = "runtime-benchmarks")]
mod benchmarks;
#[cfg(test)]
mod mock;
#[cfg(test)]
mod tests;

pub mod weights;

const LOG_TARGET: &str = "mbm::vesting";
const PALLET_MIGRATIONS_ID: &[u8; 18] = b"pallet-vesting-mbm";

pub struct LazyMigration<T, W: weights::WeightInfo>(core::marker::PhantomData<(T, W)>);

impl<T: pallet_vesting::Config, W: weights::WeightInfo> SteppedMigration for LazyMigration<T, W> {
    type Cursor = <T as frame_system::Config>::AccountId;
    // Without the explicit length here the construction of the ID would not be infallible.
    type Identifier = MigrationId<18>;

    /// The identifier of this migration. Which should be globally unique.
    fn id() -> Self::Identifier {
        MigrationId {
            pallet_id: *PALLET_MIGRATIONS_ID,
            version_from: 0,
            version_to: 1,
        }
    }

    fn step(
        mut cursor: Option<Self::Cursor>,
        meter: &mut WeightMeter,
    ) -> Result<Option<Self::Cursor>, SteppedMigrationError> {
        let required = W::step(T::MAX_VESTING_SCHEDULES);
        // If there is not enough weight for a single step, return an error. This case can be
        // problematic if it is the first migration that ran in this block. But there is nothing
        // that we can do about it here.
        if meter.remaining().any_lt(required) {
            return Err(SteppedMigrationError::InsufficientWeight { required });
        }

        let mut count = 0u32;
        let para_block_number = frame_system::Pallet::<T>::block_number();
        let current_block_number = T::BlockNumberProvider::current_block_number();

        // We loop here to do as much progress as possible per step.
        loop {
            // stop when remaining weight is lower than step max weight
            if meter.remaining().any_lt(required) {
                break;
            }

            let mut iter = if let Some(last_key) = cursor {
                // If a cursor is provided, start iterating from the stored value
                // corresponding to the last key processed in the previous step.
                // Note that this only works if the old and the new map use the same way to hash
                // storage keys.
                Vesting::<T>::iter_from(Vesting::<T>::hashed_key_for(last_key))
            } else {
                // If no cursor is provided, start iterating from the beginning.
                Vesting::<T>::iter()
            };

            // If there's a next item in the iterator, perform the migration.
            if let Some((ref last_key, mut schedules)) = iter.next() {
                for schedule in schedules.iter_mut() {
                    // remaining locked balance
                    let locked = schedule.locked_at::<T::BlockNumberToBalance>(para_block_number);
                    // reduce unlock `per_block` into half
                    let per_block = Percent::from_percent(50) * schedule.per_block();
                    // remaining blocks to start vesting if vesting hasn't started yet
                    // remaining blocks will be doubled
                    let remaining_blocks = schedule
                        .starting_block()
                        .saturating_sub(para_block_number)
                        .saturating_mul(2u32.into());
                    let start_block = current_block_number.saturating_add(remaining_blocks);

                    *schedule = VestingInfo::new(locked, per_block, start_block);
                }

                // consume the exact weight
                meter.consume(W::step(schedules.len().saturated_into()));

                // Override vesting schedules
                Vesting::<T>::insert(last_key, schedules);

                // inc counter
                count.saturating_inc();

                // Return the processed key as the new cursor.
                cursor = Some(last_key.clone())
            } else {
                // Signal that the migration is complete (no more items to process).
                cursor = None;
                break;
            }
        }
        log::debug!(target: LOG_TARGET, "migrated {count:?} entries");
        Ok(cursor)
    }
}

#[frame_support::pallet]
pub mod pallet {
    use super::*;

    #[pallet::pallet]
    #[pallet::without_storage_info]
    pub struct Pallet<T>(_);

    #[pallet::config]
    pub trait Config: frame_system::Config + pallet_vesting::Config {}
}