pallet_staking_reward_fn/lib.rs
1// This file is part of Substrate.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: Apache-2.0
5
6// Licensed under the Apache License, Version 2.0 (the "License");
7// you may not use this file except in compliance with the License.
8// You may obtain a copy of the License at
9//
10// http://www.apache.org/licenses/LICENSE-2.0
11//
12// Unless required by applicable law or agreed to in writing, software
13// distributed under the License is distributed on an "AS IS" BASIS,
14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15// See the License for the specific language governing permissions and
16// limitations under the License.
17
18#![cfg_attr(not(feature = "std"), no_std)]
19
20//! Useful function for inflation for nominated proof of stake.
21
22use sp_arithmetic::{
23 biguint::BigUint,
24 traits::{SaturatedConversion, Zero},
25 PerThing, Perquintill,
26};
27
28/// Compute yearly inflation using function
29///
30/// ```ignore
31/// I(x) = for x between 0 and x_ideal: x / x_ideal,
32/// for x between x_ideal and 1: 2^((x_ideal - x) / d)
33/// ```
34///
35/// where:
36/// * x is the stake rate, i.e. fraction of total issued tokens that actively staked behind
37/// validators.
38/// * d is the falloff or `decay_rate`
39/// * x_ideal: the ideal stake rate.
40///
41/// The result is meant to be scaled with minimum inflation and maximum inflation.
42///
43/// (as detailed
44/// [here](https://research.web3.foundation/Polkadot/overview/token-economics#inflation-model-with-parachains))
45///
46/// Arguments are:
47/// * `stake`: The fraction of total issued tokens that actively staked behind validators. Known as
48/// `x` in the literature. Must be between 0 and 1.
49/// * `ideal_stake`: The fraction of total issued tokens that should be actively staked behind
50/// validators. Known as `x_ideal` in the literature. Must be between 0 and 1.
51/// * `falloff`: Known as `decay_rate` in the literature. A co-efficient dictating the strength of
52/// the global incentivization to get the `ideal_stake`. A higher number results in less typical
53/// inflation at the cost of greater volatility for validators. Must be more than 0.01.
54pub fn compute_inflation<P: PerThing>(stake: P, ideal_stake: P, falloff: P) -> P {
55 if stake < ideal_stake {
56 // ideal_stake is more than 0 because it is strictly more than stake
57 return stake / ideal_stake
58 }
59
60 if falloff < P::from_percent(1.into()) {
61 log::error!("Invalid inflation computation: falloff less than 1% is not supported");
62 return PerThing::zero()
63 }
64
65 let accuracy = {
66 let mut a = BigUint::from(Into::<u128>::into(P::ACCURACY));
67 a.lstrip();
68 a
69 };
70
71 let mut falloff = BigUint::from(falloff.deconstruct().into());
72 falloff.lstrip();
73
74 let ln2 = {
75 /// `ln(2)` expressed in as perquintillionth.
76 const LN2: u64 = 0_693_147_180_559_945_309;
77 let ln2 = P::from_rational(LN2.into(), Perquintill::ACCURACY.into());
78 BigUint::from(ln2.deconstruct().into())
79 };
80
81 // falloff is stripped above.
82 let ln2_div_d = div_by_stripped(ln2.mul(&accuracy), &falloff);
83
84 let inpos_param = INPoSParam {
85 x_ideal: BigUint::from(ideal_stake.deconstruct().into()),
86 x: BigUint::from(stake.deconstruct().into()),
87 accuracy,
88 ln2_div_d,
89 };
90
91 let res = compute_taylor_serie_part(&inpos_param);
92
93 match u128::try_from(res.clone()) {
94 Ok(res) if res <= Into::<u128>::into(P::ACCURACY) => P::from_parts(res.saturated_into()),
95 // If result is beyond bounds there is nothing we can do
96 _ => {
97 log::error!("Invalid inflation computation: unexpected result {:?}", res);
98 P::zero()
99 },
100 }
101}
102
103/// Internal struct holding parameter info alongside other cached value.
104///
105/// All expressed in part from `accuracy`
106struct INPoSParam {
107 ln2_div_d: BigUint,
108 x_ideal: BigUint,
109 x: BigUint,
110 /// Must be stripped and have no leading zeros.
111 accuracy: BigUint,
112}
113
114/// Compute `2^((x_ideal - x) / d)` using taylor series.
115///
116/// x must be strictly more than x_ideal.
117///
118/// result is expressed with accuracy `INPoSParam.accuracy`
119fn compute_taylor_serie_part(p: &INPoSParam) -> BigUint {
120 // The last computed taylor term.
121 let mut last_taylor_term = p.accuracy.clone();
122
123 // Whereas taylor sum is positive.
124 let mut taylor_sum_positive = true;
125
126 // The sum of all taylor term.
127 let mut taylor_sum = last_taylor_term.clone();
128
129 for k in 1..300 {
130 last_taylor_term = compute_taylor_term(k, &last_taylor_term, p);
131
132 if last_taylor_term.is_zero() {
133 break
134 }
135
136 let last_taylor_term_positive = k % 2 == 0;
137
138 if taylor_sum_positive == last_taylor_term_positive {
139 taylor_sum = taylor_sum.add(&last_taylor_term);
140 } else if taylor_sum >= last_taylor_term {
141 taylor_sum = taylor_sum
142 .sub(&last_taylor_term)
143 // NOTE: Should never happen as checked above
144 .unwrap_or_else(|e| e);
145 } else {
146 taylor_sum_positive = !taylor_sum_positive;
147 taylor_sum = last_taylor_term
148 .clone()
149 .sub(&taylor_sum)
150 // NOTE: Should never happen as checked above
151 .unwrap_or_else(|e| e);
152 }
153 }
154
155 if !taylor_sum_positive {
156 return BigUint::zero()
157 }
158
159 taylor_sum.lstrip();
160 taylor_sum
161}
162
163/// Return the absolute value of k-th taylor term of `2^((x_ideal - x))/d` i.e.
164/// `((x - x_ideal) * ln(2) / d)^k / k!`
165///
166/// x must be strictly more x_ideal.
167///
168/// We compute the term from the last term using this formula:
169///
170/// `((x - x_ideal) * ln(2) / d)^k / k! == previous_term * (x - x_ideal) * ln(2) / d / k`
171///
172/// `previous_taylor_term` and result are expressed with accuracy `INPoSParam.accuracy`
173fn compute_taylor_term(k: u32, previous_taylor_term: &BigUint, p: &INPoSParam) -> BigUint {
174 let x_minus_x_ideal =
175 p.x.clone()
176 .sub(&p.x_ideal)
177 // NOTE: Should never happen, as x must be more than x_ideal
178 .unwrap_or_else(|_| BigUint::zero());
179
180 let res = previous_taylor_term.clone().mul(&x_minus_x_ideal).mul(&p.ln2_div_d).div_unit(k);
181
182 // p.accuracy is stripped by definition.
183 let res = div_by_stripped(res, &p.accuracy);
184 let mut res = div_by_stripped(res, &p.accuracy);
185
186 res.lstrip();
187 res
188}
189
190/// Compute a div b.
191///
192/// requires `b` to be stripped and have no leading zeros.
193fn div_by_stripped(mut a: BigUint, b: &BigUint) -> BigUint {
194 a.lstrip();
195
196 if b.len() == 0 {
197 log::error!("Computation error: Invalid division");
198 return BigUint::zero()
199 }
200
201 if b.len() == 1 {
202 return a.div_unit(b.checked_get(0).unwrap_or(1))
203 }
204
205 if b.len() > a.len() {
206 return BigUint::zero()
207 }
208
209 if b.len() == a.len() {
210 // 100_000^2 is more than 2^32-1, thus `new_a` has more limbs than `b`.
211 let mut new_a = a.mul(&BigUint::from(100_000u64.pow(2)));
212 new_a.lstrip();
213
214 debug_assert!(new_a.len() > b.len());
215 return new_a
216 .div(b, false)
217 .map(|res| res.0)
218 .unwrap_or_else(BigUint::zero)
219 .div_unit(100_000)
220 .div_unit(100_000)
221 }
222
223 a.div(b, false).map(|res| res.0).unwrap_or_else(BigUint::zero)
224}