Refactor: fixed point arithmetic for SRML. (#3456)

* Macro-ify perthings.

* Refactor fixed64

* Half-workign phragmen refactor.

* Finalize phragmen refactor.

* Fix creation of perquintill

* Fix build errors

* Line-width

* Fix more build errors.

* Line-width

* Fix offence test

* Resolve all TODOs.

* Apply suggestions from code review

Co-Authored-By: Gavin Wood <gavin@parity.io>
Co-Authored-By: thiolliere <gui.thiolliere@gmail.com>

* Fix most of the review comments.

* Updates to multiply by rational

* Fxi build

* Fix abs issue with Fixed64

* Fix tests and improvements.

* Fix build

* Remove more tests from staking.

* Review comments.

* Add fuzzing stuff.

* Better fuzzing

* Better doc.

* Bump.

* Master.into()

* A bit more hardening.

* Final nits.

* Update lock

* Fix indent.

* Revert lock file.

* Bump.
This commit is contained in:
Kian Paimani
2019-09-25 11:21:05 +02:00
committed by GitHub
parent 87688aadaa
commit 1c15ca6ad1
19 changed files with 1909 additions and 961 deletions
+3 -3
View File
@@ -59,13 +59,13 @@ impl<'a> PiecewiseLinear<'a> {
let delta_y = multiply_by_rational_saturating(
abs_sub(n.clone(), prev.0 * d.clone()),
abs_sub(next.1.into_parts(), prev.1.into_parts()),
abs_sub(next.1.deconstruct(), prev.1.deconstruct()),
// Must not saturate as prev abscissa > next abscissa
next.0.into_parts().saturating_sub(prev.0.into_parts()),
next.0.deconstruct().saturating_sub(prev.0.deconstruct()),
);
// If both substration are same sign then result is positive
if (n > prev.0 * d.clone()) == (next.1.into_parts() > prev.1.into_parts()) {
if (n > prev.0 * d.clone()) == (next.1.deconstruct() > prev.1.deconstruct()) {
(prev.1 * d).saturating_add(delta_y)
// Otherwise result is negative
} else {
+45 -486
View File
@@ -17,7 +17,6 @@
//! Runtime Modules shared primitive types.
#![warn(missing_docs)]
#![cfg_attr(not(feature = "std"), no_std)]
#[doc(hidden)]
@@ -37,10 +36,10 @@ pub use app_crypto;
#[cfg(feature = "std")]
pub use runtime_io::{StorageOverlay, ChildrenStorageOverlay};
use rstd::{prelude::*, ops, convert::{TryInto, TryFrom}};
use rstd::prelude::*;
use rstd::convert::TryFrom;
use primitives::{crypto, ed25519, sr25519, hash::{H256, H512}};
use codec::{Encode, Decode, CompactAs};
use traits::{SaturatedConversion, UniqueSaturatedInto, Saturating, Bounded, CheckedSub, CheckedAdd};
use codec::{Encode, Decode};
#[cfg(feature = "std")]
pub mod testing;
@@ -51,6 +50,7 @@ pub mod curve;
pub mod generic;
pub mod transaction_validity;
pub mod sr_arithmetic;
/// Re-export these since they're only "kind of" generic.
pub use generic::{DigestItem, Digest};
@@ -59,6 +59,14 @@ pub use generic::{DigestItem, Digest};
pub use primitives::crypto::{key_types, KeyTypeId, CryptoType};
pub use app_crypto::RuntimeAppPublic;
/// Re-export arithmetic stuff.
pub use sr_arithmetic::{
Perquintill, Perbill, Permill, Percent,
Rational128, Fixed64
};
/// Re-export 128 bit helpers from sr_arithmetic
pub use sr_arithmetic::helpers_128bit;
/// An abstraction over justification for a block's validity under a consensus algorithm.
///
/// Essentially a finality proof. The exact formulation will vary between consensus
@@ -152,360 +160,6 @@ impl BuildStorage for (StorageOverlay, ChildrenStorageOverlay) {
/// Consensus engine unique ID.
pub type ConsensusEngineId = [u8; 4];
/// Permill is parts-per-million (i.e. after multiplying by this, divide by 1000000).
#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug, Ord, PartialOrd))]
#[derive(Encode, Decode, CompactAs, Default, Copy, Clone, PartialEq, Eq)]
pub struct Permill(u32);
impl Permill {
/// Nothing.
pub fn zero() -> Self { Self(0) }
/// `true` if this is nothing.
pub fn is_zero(&self) -> bool { self.0 == 0 }
/// Everything.
pub fn one() -> Self { Self(1_000_000) }
/// create a new raw instance. This can be called at compile time.
pub const fn from_const_parts(parts: u32) -> Self {
Self([parts, 1_000_000][(parts > 1_000_000) as usize])
}
/// From an explicitly defined number of parts per maximum of the type.
pub fn from_parts(parts: u32) -> Self { Self::from_const_parts(parts) }
/// Converts from a percent. Equal to `x / 100`.
pub const fn from_percent(x: u32) -> Self { Self([x, 100][(x > 100) as usize] * 10_000) }
/// Converts a fraction into `Permill`.
#[cfg(feature = "std")]
pub fn from_fraction(x: f64) -> Self { Self((x * 1_000_000.0) as u32) }
/// Approximate the fraction `p/q` into a per million fraction
pub fn from_rational_approximation<N>(p: N, q: N) -> Self
where N: traits::SimpleArithmetic + Clone
{
let p = p.min(q.clone());
let factor = (q.clone() / 1_000_000u32.into()).max(1u32.into());
// Conversion can't overflow as p < q so ( p / (q/million)) < million
let p_reduce: u32 = (p / factor.clone()).try_into().unwrap_or_else(|_| panic!());
let q_reduce: u32 = (q / factor.clone()).try_into().unwrap_or_else(|_| panic!());
let part = p_reduce as u64 * 1_000_000u64 / q_reduce as u64;
Permill(part as u32)
}
}
impl<N> ops::Mul<N> for Permill
where
N: Clone + From<u32> + UniqueSaturatedInto<u32> + ops::Rem<N, Output=N>
+ ops::Div<N, Output=N> + ops::Mul<N, Output=N> + ops::Add<N, Output=N>,
{
type Output = N;
fn mul(self, b: N) -> Self::Output {
let million: N = 1_000_000.into();
let part: N = self.0.into();
let rem_multiplied_divided = {
let rem = b.clone().rem(million.clone());
// `rem` is inferior to one million, thus it fits into u32
let rem_u32 = rem.saturated_into::<u32>();
// `self` and `rem` are inferior to one million, thus the product is less than 10^12
// and fits into u64
let rem_multiplied_u64 = rem_u32 as u64 * self.0 as u64;
// `rem_multiplied_u64` is less than 10^12 therefore divided by a million it fits into
// u32
let rem_multiplied_divided_u32 = (rem_multiplied_u64 / 1_000_000) as u32;
// `rem_multiplied_divided` is inferior to b, thus it can be converted back to N type
rem_multiplied_divided_u32.into()
};
(b / million) * part + rem_multiplied_divided
}
}
#[cfg(feature = "std")]
impl From<f64> for Permill {
fn from(x: f64) -> Permill {
Permill::from_fraction(x)
}
}
#[cfg(feature = "std")]
impl From<f32> for Permill {
fn from(x: f32) -> Permill {
Permill::from_fraction(x as f64)
}
}
/// Perbill is parts-per-billion. It stores a value between 0 and 1 in fixed point and
/// provides a means to multiply some other value by that.
#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug))]
#[derive(Encode, Decode, CompactAs, Default, Copy, Clone, PartialEq, Eq, Ord, PartialOrd)]
pub struct Perbill(u32);
impl Perbill {
/// Nothing.
pub fn zero() -> Self { Self(0) }
/// `true` if this is nothing.
pub fn is_zero(&self) -> bool { self.0 == 0 }
/// Everything.
pub fn one() -> Self { Self(1_000_000_000) }
/// create a new raw instance. This can be called at compile time.
pub const fn from_const_parts(parts: u32) -> Self {
Self([parts, 1_000_000_000][(parts > 1_000_000_000) as usize])
}
/// From an explicitly defined number of parts per maximum of the type.
pub fn from_parts(parts: u32) -> Self { Self::from_const_parts(parts) }
/// Converts from a percent. Equal to `x / 100`.
pub const fn from_percent(x: u32) -> Self { Self([x, 100][(x > 100) as usize] * 10_000_000) }
/// Construct new instance where `x` is in millionths. Value equivalent to `x / 1,000,000`.
pub fn from_millionths(x: u32) -> Self { Self(x.min(1_000_000) * 1000) }
#[cfg(feature = "std")]
/// Construct new instance whose value is equal to `x` (between 0 and 1).
pub fn from_fraction(x: f64) -> Self { Self((x.max(0.0).min(1.0) * 1_000_000_000.0) as u32) }
/// Approximate the fraction `p/q` into a per billion fraction
pub fn from_rational_approximation<N>(p: N, q: N) -> Self
where N: traits::SimpleArithmetic + Clone
{
let p = p.min(q.clone());
let factor = (q.clone() / 1_000_000_000u32.into()).max(1u32.into());
// Conversion can't overflow as p < q so ( p / (q/billion)) < billion
let p_reduce: u32 = (p / factor.clone()).try_into().unwrap_or_else(|_| panic!());
let q_reduce: u32 = (q / factor.clone()).try_into().unwrap_or_else(|_| panic!());
let part = p_reduce as u64 * 1_000_000_000u64 / q_reduce as u64;
Perbill(part as u32)
}
/// Return the product of multiplication of this value by itself.
pub fn square(self) -> Self {
let p: u64 = self.0 as u64 * self.0 as u64;
let q: u64 = 1_000_000_000 * 1_000_000_000;
Self::from_rational_approximation(p, q)
}
/// Take out the raw parts-per-billions.
pub fn into_parts(self) -> u32 {
self.0
}
}
impl<N> ops::Mul<N> for Perbill
where
N: Clone + From<u32> + UniqueSaturatedInto<u32> + ops::Rem<N, Output=N>
+ ops::Div<N, Output=N> + ops::Mul<N, Output=N> + ops::Add<N, Output=N>,
{
type Output = N;
fn mul(self, b: N) -> Self::Output {
let billion: N = 1_000_000_000.into();
let part: N = self.0.into();
let rem_multiplied_divided = {
let rem = b.clone().rem(billion.clone());
// `rem` is inferior to one billion, thus it fits into u32
let rem_u32 = rem.saturated_into::<u32>();
// `self` and `rem` are inferior to one billion, thus the product is less than 10^18
// and fits into u64
let rem_multiplied_u64 = rem_u32 as u64 * self.0 as u64;
// `rem_multiplied_u64` is less than 10^18 therefore divided by a billion it fits into
// u32
let rem_multiplied_divided_u32 = (rem_multiplied_u64 / 1_000_000_000) as u32;
// `rem_multiplied_divided` is inferior to b, thus it can be converted back to N type
rem_multiplied_divided_u32.into()
};
(b / billion) * part + rem_multiplied_divided
}
}
#[cfg(feature = "std")]
impl From<f64> for Perbill {
fn from(x: f64) -> Perbill {
Perbill::from_fraction(x)
}
}
#[cfg(feature = "std")]
impl From<f32> for Perbill {
fn from(x: f32) -> Perbill {
Perbill::from_fraction(x as f64)
}
}
/// A fixed point number by the scale of 1 billion.
///
/// cannot hold a value larger than +-`9223372036854775807 / 1_000_000_000` (~9 billion).
#[cfg_attr(feature = "std", derive(Debug))]
#[derive(Encode, Decode, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct Fixed64(i64);
/// The maximum value of the `Fixed64` type
const DIV: i64 = 1_000_000_000;
impl Fixed64 {
/// creates self from a natural number.
///
/// Note that this might be lossy.
pub fn from_natural(int: i64) -> Self {
Self(int.saturating_mul(DIV))
}
/// Return the accuracy of the type. Given that this function returns the value `X`, it means
/// that an instance composed of `X` parts (`Fixed64::from_parts(X)`) is equal to `1`.
pub fn accuracy() -> i64 {
DIV
}
/// creates self from a rational number. Equal to `n/d`.
///
/// Note that this might be lossy.
pub fn from_rational(n: i64, d: u64) -> Self {
Self((n as i128 * DIV as i128 / (d as i128).max(1)).try_into().unwrap_or(Bounded::max_value()))
}
/// Performs a saturated multiply and accumulate.
///
/// Returns a saturated `n + (self * n)`.
/// TODO: generalize this to any weight type. #3189
pub fn saturated_multiply_accumulate(&self, int: u32) -> u32 {
let parts = self.0;
let positive = parts > 0;
// natural parts might overflow.
let natural_parts = self.clone().saturated_into::<u32>();
// fractional parts can always fit into u32.
let perbill_parts = (parts.abs() % DIV) as u32;
let n = int.saturating_mul(natural_parts);
let p = Perbill::from_parts(perbill_parts) * int;
// everything that needs to be either added or subtracted from the original weight.
let excess = n.saturating_add(p);
if positive {
int.saturating_add(excess)
} else {
int.saturating_sub(excess)
}
}
/// Raw constructor. Equal to `parts / 1_000_000_000`.
pub fn from_parts(parts: i64) -> Self {
Self(parts)
}
}
impl UniqueSaturatedInto<u32> for Fixed64 {
/// Note that the maximum value of Fixed64 might be more than what can fit in u32. This is hence,
/// expected to be lossy.
fn unique_saturated_into(self) -> u32 {
(self.0.abs() / DIV).try_into().unwrap_or(Bounded::max_value())
}
}
impl Saturating for Fixed64 {
fn saturating_add(self, rhs: Self) -> Self {
Self(self.0.saturating_add(rhs.0))
}
fn saturating_mul(self, rhs: Self) -> Self {
Self(self.0.saturating_mul(rhs.0) / DIV)
}
fn saturating_sub(self, rhs: Self) -> Self {
Self(self.0.saturating_sub(rhs.0))
}
}
/// Note that this is a standard, _potentially-panicking_, implementation. Use `Saturating` trait
/// for safe addition.
impl ops::Add for Fixed64 {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
Self(self.0 + rhs.0)
}
}
/// Note that this is a standard, _potentially-panicking_, implementation. Use `Saturating` trait
/// for safe subtraction.
impl ops::Sub for Fixed64 {
type Output = Self;
fn sub(self, rhs: Self) -> Self::Output {
Self(self.0 - rhs.0)
}
}
impl CheckedSub for Fixed64 {
fn checked_sub(&self, rhs: &Self) -> Option<Self> {
if let Some(v) = self.0.checked_sub(rhs.0) {
Some(Self(v))
} else {
None
}
}
}
impl CheckedAdd for Fixed64 {
fn checked_add(&self, rhs: &Self) -> Option<Self> {
if let Some(v) = self.0.checked_add(rhs.0) {
Some(Self(v))
} else {
None
}
}
}
/// PerU128 is parts-per-u128-max-value. It stores a value between 0 and 1 in fixed point.
#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug))]
#[derive(Encode, Decode, CompactAs, Default, Copy, Clone, PartialEq, Eq)]
pub struct PerU128(u128);
const U128: u128 = u128::max_value();
impl PerU128 {
/// Nothing.
pub fn zero() -> Self { Self(0) }
/// `true` if this is nothing.
pub fn is_zero(&self) -> bool { self.0 == 0 }
/// Everything.
pub fn one() -> Self { Self(U128) }
/// From an explicitly defined number of parts per maximum of the type.
pub fn from_parts(x: u128) -> Self { Self(x) }
/// Construct new instance where `x` is denominator and the nominator is 1.
pub fn from_xth(x: u128) -> Self { Self(U128/x.max(1)) }
}
impl ::rstd::ops::Deref for PerU128 {
type Target = u128;
fn deref(&self) -> &u128 {
&self.0
}
}
/// Signature verify that can work with any known signature types..
#[derive(Eq, PartialEq, Clone, Encode, Decode)]
#[cfg_attr(feature = "std", derive(Debug))]
@@ -869,6 +523,37 @@ macro_rules! impl_outer_config {
}
}
/// Checks that `$x` is equal to `$y` with an error rate of `$error`.
///
/// # Example
///
/// ```rust
/// # fn main() {
/// sr_primitives::assert_eq_error_rate!(10, 10, 0);
/// sr_primitives::assert_eq_error_rate!(10, 11, 1);
/// sr_primitives::assert_eq_error_rate!(12, 10, 2);
/// # }
/// ```
///
/// ```rust,should_panic
/// # fn main() {
/// sr_primitives::assert_eq_error_rate!(12, 10, 1);
/// # }
/// ```
#[macro_export]
#[cfg(feature = "std")]
macro_rules! assert_eq_error_rate {
($x:expr, $y:expr, $error:expr $(,)?) => {
assert!(
($x) >= (($y) - ($error)) && ($x) <= (($y) + ($error)),
"{:?} != {:?} (with error rate {:?})",
$x,
$y,
$error,
);
};
}
/// Simple blob to hold an extrinsic without committing to its format and ensure it is serialized
/// correctly.
#[derive(PartialEq, Eq, Clone, Default, Encode, Decode)]
@@ -909,41 +594,8 @@ pub fn print(print: impl traits::Printable) {
#[cfg(test)]
mod tests {
use super::DispatchError;
use crate::codec::{Encode, Decode};
use super::{Perbill, Permill};
macro_rules! per_thing_upper_test {
($num_type:tt, $per:tt) => {
// multiplication from all sort of from_percent
assert_eq!($per::from_percent(100) * $num_type::max_value(), $num_type::max_value());
assert_eq!(
$per::from_percent(99) * $num_type::max_value(),
((Into::<U256>::into($num_type::max_value()) * 99u32) / 100u32).as_u128() as $num_type
);
assert_eq!($per::from_percent(50) * $num_type::max_value(), $num_type::max_value() / 2);
assert_eq!($per::from_percent(1) * $num_type::max_value(), $num_type::max_value() / 100);
assert_eq!($per::from_percent(0) * $num_type::max_value(), 0);
// multiplication with bounds
assert_eq!($per::one() * $num_type::max_value(), $num_type::max_value());
assert_eq!($per::zero() * $num_type::max_value(), 0);
// from_rational_approximation
assert_eq!(
$per::from_rational_approximation(u128::max_value() - 1, u128::max_value()),
$per::one(),
);
assert_eq!(
$per::from_rational_approximation(u128::max_value()/3, u128::max_value()),
$per::from_parts($per::one().0/3),
);
assert_eq!(
$per::from_rational_approximation(1, u128::max_value()),
$per::zero(),
);
}
}
use crate::DispatchError;
use codec::{Encode, Decode};
#[test]
fn opaque_extrinsic_serialization() {
@@ -951,80 +603,6 @@ mod tests {
assert_eq!(serde_json::to_string(&ex).unwrap(), "\"0x1001020304\"".to_owned());
}
#[test]
fn compact_permill_perbill_encoding() {
let tests = [(0u32, 1usize), (63, 1), (64, 2), (16383, 2), (16384, 4), (1073741823, 4), (1073741824, 5), (u32::max_value(), 5)];
for &(n, l) in &tests {
let compact: crate::codec::Compact<Permill> = Permill(n).into();
let encoded = compact.encode();
assert_eq!(encoded.len(), l);
let decoded = <crate::codec::Compact<Permill>>::decode(&mut & encoded[..]).unwrap();
let permill: Permill = decoded.into();
assert_eq!(permill, Permill(n));
let compact: crate::codec::Compact<Perbill> = Perbill(n).into();
let encoded = compact.encode();
assert_eq!(encoded.len(), l);
let decoded = <crate::codec::Compact<Perbill>>::decode(&mut & encoded[..]).unwrap();
let perbill: Perbill = decoded.into();
assert_eq!(perbill, Perbill(n));
}
}
#[derive(Encode, Decode, PartialEq, Eq, Debug)]
struct WithCompact<T: crate::codec::HasCompact> {
data: T,
}
#[test]
fn test_has_compact_permill() {
let data = WithCompact { data: Permill(1) };
let encoded = data.encode();
assert_eq!(data, WithCompact::<Permill>::decode(&mut &encoded[..]).unwrap());
}
#[test]
fn test_has_compact_perbill() {
let data = WithCompact { data: Perbill(1) };
let encoded = data.encode();
assert_eq!(data, WithCompact::<Perbill>::decode(&mut &encoded[..]).unwrap());
}
#[test]
fn per_things_should_work() {
use super::{Perbill, Permill};
use primitive_types::U256;
per_thing_upper_test!(u32, Perbill);
per_thing_upper_test!(u64, Perbill);
per_thing_upper_test!(u128, Perbill);
per_thing_upper_test!(u32, Permill);
per_thing_upper_test!(u64, Permill);
per_thing_upper_test!(u128, Permill);
}
#[test]
fn per_things_operate_in_output_type() {
assert_eq!(Perbill::one() * 255_u64, 255);
}
#[test]
fn per_things_one_minus_one_part() {
use primitive_types::U256;
assert_eq!(
Perbill::from_parts(999_999_999) * std::u128::MAX,
((Into::<U256>::into(std::u128::MAX) * 999_999_999u32) / 1_000_000_000u32).as_u128()
);
assert_eq!(
Permill::from_parts(999_999) * std::u128::MAX,
((Into::<U256>::into(std::u128::MAX) * 999_999u32) / 1_000_000u32).as_u128()
);
}
#[test]
fn dispatch_error_encoding() {
let error = DispatchError {
@@ -1044,23 +622,4 @@ mod tests {
},
);
}
#[test]
fn per_bill_square() {
const FIXTURES: &[(u32, u32)] = &[
(0, 0),
(1250000, 1562), // (0.00125, 0.000001562)
(255300000, 65178090), // (0.2553, 0.06517809)
(500000000, 250000000), // (0.5, 0.25)
(999995000, 999990000), // (0.999995, 0.999990000, but ideally 0.99999000002)
(1000000000, 1000000000),
];
for &(x, r) in FIXTURES {
assert_eq!(
Perbill::from_parts(x).square(),
Perbill::from_parts(r),
);
}
}
}
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -188,7 +188,7 @@ impl WeightMultiplier {
/// build self from raw parts per billion.
#[cfg(feature = "std")]
pub fn from_parts(parts: i64) -> Self {
Self(Fixed64(parts))
Self(Fixed64::from_parts(parts))
}
/// build self from a fixed64 value.