/home/runner/work/creditcoin/creditcoin/pallets/difficulty/src/lib.rs
Line | Count | Source (jump to first uncovered line) |
1 | 1 | #![cfg_attr(not(feature = "std"), no_std)]#![cfg_attr(not(feature = "std"), no_std)] |
2 | | |
3 | | use frame_support::{pallet_prelude::*, sp_runtime, traits::OnTimestampSet}; |
4 | | pub use pallet::*; |
5 | | use primitives::Difficulty; |
6 | | use sp_arithmetic::traits::BaseArithmetic; |
7 | | use sp_runtime::traits::{SaturatedConversion, UniqueSaturatedInto}; |
8 | | |
9 | | #[cfg(test)] |
10 | | mod mock; |
11 | | |
12 | | #[allow(clippy::unnecessary_cast)] |
13 | | pub mod weights; |
14 | | |
15 | | #[cfg(test)] |
16 | | mod tests; |
17 | | |
18 | | mod benchmarking; |
19 | | |
20 | 7 | #[derive(Clone3 , Encode, D2 ecode2 , Eq, P0 artialEq0 , RuntimeDebug0 , TypeInfo0 , MaxEncodedLen0 )] |
21 | 0 | pub struct DifficultyAndTimestamp<Moment> { |
22 | | difficulty: Difficulty, |
23 | | timestamp: Moment, |
24 | | } |
25 | | |
26 | 0 | #[frame_support::pallet] |
27 | 0 | pub mod pallet { |
28 | 0 | use frame_support::{ |
29 | 0 | pallet_prelude::*, |
30 | 0 | sp_runtime::traits::{MaybeSerializeDeserialize, SaturatedConversion}, |
31 | 0 | }; |
32 | 0 | use frame_system::pallet_prelude::*; |
33 | 0 | use primitives::Difficulty; |
34 | 0 | use sp_arithmetic::traits::{BaseArithmetic, UniqueSaturatedInto, Zero}; |
35 | 0 |
|
36 | 0 | use crate::DifficultyAndTimestamp; |
37 | 0 |
|
38 | 0 | #[pallet::config] |
39 | 0 | pub trait Config: frame_system::Config { |
40 | 0 | type Moment: Parameter |
41 | 0 | + Default |
42 | 0 | + Copy |
43 | 0 | + MaxEncodedLen |
44 | 0 | + scale_info::StaticTypeInfo |
45 | 0 | + SaturatedConversion |
46 | 0 | + BaseArithmetic |
47 | 0 | + UniqueSaturatedInto<i64> |
48 | 0 | + MaybeSerializeDeserialize; |
49 | 0 |
|
50 | 0 | type WeightInfo: WeightInfo; |
51 | 0 | } |
52 | 0 |
|
53 | 0 | pub trait WeightInfo { |
54 | 0 | fn set_target_block_time() -> Weight; |
55 | 0 | fn set_adjustment_period() -> Weight; |
56 | 0 | } |
57 | 0 |
|
58 | 0 | #[pallet::pallet] |
59 | 0 | #[pallet::generate_store(pub(super) trait Store)] |
60 | 0 | pub struct Pallet<T>(_); |
61 | 0 |
|
62 | 0 | #[pallet::genesis_config] |
63 | 0 | pub struct GenesisConfig<T: Config> { |
64 | 0 | pub initial_difficulty: Difficulty, |
65 | 0 | pub target_time: T::Moment, |
66 | 0 | pub difficulty_adjustment_period: i64, |
67 | 0 | } |
68 | 0 |
|
69 | 0 | #[cfg(feature = "std")] |
70 | 0 | impl<T: Config> Default for GenesisConfig<T> { |
71 | 4 | fn default() -> Self { |
72 | 4 | Self { |
73 | 4 | initial_difficulty: Difficulty::from(1_000_000), |
74 | 4 | target_time: T::Moment::default(), |
75 | 4 | difficulty_adjustment_period: 28, |
76 | 4 | } |
77 | 4 | } |
78 | 0 | } |
79 | 0 |
|
80 | 12 | #[pallet::error] |
81 | 0 | pub enum Error<T> { |
82 | 0 | ZeroTargetTime, |
83 | 0 | ZeroAdjustmentPeriod, |
84 | 0 | NegativeAdjustmentPeriod, |
85 | 0 | } |
86 | 0 |
|
87 | 2 | #[pallet::genesis_build] |
88 | 0 | impl<T: Config> GenesisBuild<T> for GenesisConfig<T> { |
89 | 2 | fn build(&self) { |
90 | 2 | CurrentDifficulty::<T>::put(self.initial_difficulty); |
91 | 2 | TargetBlockTime::<T>::put(self.target_time); |
92 | 2 | DifficultyAdjustmentPeriod::<T>::put(self.difficulty_adjustment_period); |
93 | 2 | } |
94 | 0 | } |
95 | 0 |
|
96 | 13 | #[pallet::storage] |
97 | 0 | #[pallet::getter(fn previous_difficulties_and_timestamps)] |
98 | 0 | pub type PreviousDifficultiesAndTimestamps<T> = StorageValue< |
99 | 0 | _, |
100 | 0 | BoundedVec<DifficultyAndTimestamp<<T as Config>::Moment>, ConstU32<2>>, |
101 | 0 | ValueQuery, |
102 | 0 | >; |
103 | 0 |
|
104 | 12 | #[pallet::storage] |
105 | 0 | #[pallet::getter(fn difficulty)] |
106 | 0 | pub type CurrentDifficulty<T> = StorageValue<_, Difficulty, ValueQuery>; |
107 | 0 |
|
108 | 8 | #[pallet::storage] |
109 | 0 | pub type TargetBlockTime<T: Config> = StorageValue<_, T::Moment, ValueQuery>; |
110 | 0 |
|
111 | 8 | #[pallet::storage] |
112 | 0 | pub type DifficultyAdjustmentPeriod<T: Config> = StorageValue<_, i64, ValueQuery>; |
113 | 0 |
|
114 | 0 | #[pallet::call] |
115 | | impl<T: Config> Pallet<T> { |
116 | | #[pallet::weight(<T as Config>::WeightInfo::set_target_block_time())] |
117 | | pub fn set_target_block_time( |
118 | | origin: OriginFor<T>, |
119 | | target_time: T::Moment, |
120 | | ) -> DispatchResult { |
121 | 4 | ensure_root(origin)?2 ; |
122 | | |
123 | 2 | ensure!(!target_time.is_zero(), Error::<T>::ZeroTargetTime1 ); |
124 | | |
125 | 1 | TargetBlockTime::<T>::put(target_time); |
126 | 1 | |
127 | 1 | Ok(()) |
128 | 4 | } |
129 | | #[pallet::weight(<T as Config>::WeightInfo::set_adjustment_period())] |
130 | | pub fn set_adjustment_period(origin: OriginFor<T>, period: i64) -> DispatchResult { |
131 | 5 | ensure_root(origin)?2 ; |
132 | | |
133 | 3 | ensure!(period != 0, Error::<T>::ZeroAdjustmentPeriod1 ); |
134 | 2 | ensure!(period > 0, Error::<T>::NegativeAdjustmentPeriod1 ); |
135 | | |
136 | 1 | DifficultyAdjustmentPeriod::<T>::put(period); |
137 | 1 | |
138 | 1 | Ok(()) |
139 | 5 | } |
140 | | } |
141 | | } |
142 | | |
143 | | // Adapted from zawy12's Simple EMA difficulty algorithm, license follows: |
144 | | /* |
145 | | MIT License |
146 | | |
147 | | Copyright (c) 2017 zawy12 |
148 | | |
149 | | Permission is hereby granted, free of charge, to any person obtaining a copy |
150 | | of this software and associated documentation files (the "Software"), to deal |
151 | | in the Software without restriction, including without limitation the rights |
152 | | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
153 | | copies of the Software, and to permit persons to whom the Software is |
154 | | furnished to do so, subject to the following conditions: |
155 | | |
156 | | The above copyright notice and this permission notice shall be included in all |
157 | | copies or substantial portions of the Software. |
158 | | |
159 | | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
160 | | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
161 | | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
162 | | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
163 | | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
164 | | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
165 | | SOFTWARE. |
166 | | */ |
167 | 4 | fn next_difficulty<M>( |
168 | 4 | previous: &[DifficultyAndTimestamp<M>], |
169 | 4 | target_time: M, |
170 | 4 | initial_difficulty: Difficulty, |
171 | 4 | adjustment_period: i64, |
172 | 4 | ) -> Difficulty |
173 | 4 | where |
174 | 4 | M: SaturatedConversion |
175 | 4 | + Copy |
176 | 4 | + BaseArithmetic |
177 | 4 | + UniqueSaturatedInto<i64> |
178 | 4 | + frame_support::sp_std::fmt::Debug, |
179 | 4 | { |
180 | 4 | let n = adjustment_period; |
181 | 4 | log::debug!("previous {:?}"0 , previous); |
182 | 4 | if previous.len() < 2 { |
183 | 2 | return initial_difficulty; |
184 | 2 | } |
185 | 2 | |
186 | 2 | let oldest = &previous[0]; |
187 | 2 | let newest = &previous[1]; |
188 | 2 | |
189 | 2 | let t = target_time.saturated_into::<i64>() / 1000; |
190 | 2 | log::debug!("t = {}"0 , t); |
191 | 2 | let solve_time = (newest.timestamp.saturated_into::<i64>() |
192 | 2 | - oldest.timestamp.saturated_into::<i64>()) |
193 | 2 | / 1000; |
194 | 2 | |
195 | 2 | log::debug!("solve time = {}"0 , solve_time); |
196 | 2 | let solve_time = i64::max(-5 * t, i64::min(solve_time, 6 * t)); |
197 | 2 | log::debug!("ST = {}"0 , solve_time); |
198 | 2 | let difficulty = newest.difficulty; |
199 | 2 | |
200 | 2 | let next_difficulty = (difficulty * n * t) / (n * t - t + solve_time); |
201 | 2 | |
202 | 2 | log::debug!("next difficulty = {}"0 , next_difficulty); |
203 | | |
204 | 2 | next_difficulty |
205 | 4 | } |
206 | | |
207 | | impl<T: Config> OnTimestampSet<T::Moment> for Pallet<T> { |
208 | 2 | fn on_timestamp_set(current_timestamp: T::Moment) { |
209 | 2 | let target_time = TargetBlockTime::<T>::get(); |
210 | 2 | let current_difficulty = Self::difficulty(); |
211 | 2 | let adjustment_period = DifficultyAdjustmentPeriod::<T>::get(); |
212 | 2 | |
213 | 2 | let mut previous = PreviousDifficultiesAndTimestamps::<T>::get(); |
214 | 2 | |
215 | 2 | let current = |
216 | 2 | DifficultyAndTimestamp { difficulty: current_difficulty, timestamp: current_timestamp }; |
217 | 2 | |
218 | 2 | if previous.len() < 2 { |
219 | 1 | previous.try_push(current).expect("len < 2 checked above"); |
220 | 1 | } else { |
221 | 1 | previous[0] = previous[1].clone(); |
222 | 1 | previous[1] = current; |
223 | 1 | } |
224 | | |
225 | 2 | let next_difficulty = |
226 | 2 | next_difficulty(&previous, target_time, current_difficulty, adjustment_period); |
227 | 2 | CurrentDifficulty::<T>::put(next_difficulty); |
228 | 2 | PreviousDifficultiesAndTimestamps::<T>::put(previous); |
229 | 2 | } |
230 | | } |