diff --git a/packages/rs-dpp/src/data_contract/associated_token/token_perpetual_distribution/distribution_function/encode.rs b/packages/rs-dpp/src/data_contract/associated_token/token_perpetual_distribution/distribution_function/encode.rs index 102141a6f07..0b6c6393d2c 100644 --- a/packages/rs-dpp/src/data_contract/associated_token/token_perpetual_distribution/distribution_function/encode.rs +++ b/packages/rs-dpp/src/data_contract/associated_token/token_perpetual_distribution/distribution_function/encode.rs @@ -87,7 +87,7 @@ impl Encode for DistributionFunction { n, o, start_moment: s, - c, + b, min_value, max_value, } => { @@ -98,7 +98,7 @@ impl Encode for DistributionFunction { n.encode(encoder)?; o.encode(encoder)?; s.encode(encoder)?; - c.encode(encoder)?; + b.encode(encoder)?; min_value.encode(encoder)?; max_value.encode(encoder)?; } @@ -234,8 +234,8 @@ impl Decode for DistributionFunction { let m = i64::decode(decoder)?; let n = u64::decode(decoder)?; let o = i64::decode(decoder)?; - let s = Option::::decode(decoder)?; - let c = TokenAmount::decode(decoder)?; + let start_moment = Option::::decode(decoder)?; + let b = TokenAmount::decode(decoder)?; let min_value = Option::::decode(decoder)?; let max_value = Option::::decode(decoder)?; Ok(Self::Exponential { @@ -244,8 +244,8 @@ impl Decode for DistributionFunction { m, n, o, - start_moment: s, - c, + start_moment, + b, min_value, max_value, }) @@ -384,8 +384,8 @@ impl<'de> BorrowDecode<'de> for DistributionFunction { let m = i64::borrow_decode(decoder)?; let n = u64::borrow_decode(decoder)?; let o = i64::borrow_decode(decoder)?; - let s = Option::::borrow_decode(decoder)?; - let c = TokenAmount::borrow_decode(decoder)?; + let start_moment = Option::::borrow_decode(decoder)?; + let b = TokenAmount::borrow_decode(decoder)?; let min_value = Option::::borrow_decode(decoder)?; let max_value = Option::::borrow_decode(decoder)?; Ok(Self::Exponential { @@ -394,8 +394,8 @@ impl<'de> BorrowDecode<'de> for DistributionFunction { m, n, o, - start_moment: s, - c, + start_moment, + b, min_value, max_value, }) diff --git a/packages/rs-dpp/src/data_contract/associated_token/token_perpetual_distribution/distribution_function/evaluate.rs b/packages/rs-dpp/src/data_contract/associated_token/token_perpetual_distribution/distribution_function/evaluate.rs index f88a3faf8c0..3478e904543 100644 --- a/packages/rs-dpp/src/data_contract/associated_token/token_perpetual_distribution/distribution_function/evaluate.rs +++ b/packages/rs-dpp/src/data_contract/associated_token/token_perpetual_distribution/distribution_function/evaluate.rs @@ -254,7 +254,7 @@ impl DistributionFunction { n, o, start_moment, - c, + b, min_value, max_value, } => { @@ -284,7 +284,7 @@ impl DistributionFunction { } let exponent = (*m as f64) * (diff as f64) / (*n as f64); - let value = ((*a as f64) * exponent.exp() / (*d as f64)) + (*c as f64); + let value = ((*a as f64) * exponent.exp() / (*d as f64)) + (*b as f64); if let Some(max_value) = max_value { if value.is_infinite() && value.is_sign_positive() || value > *max_value as f64 { @@ -346,22 +346,73 @@ impl DistributionFunction { return Err(ProtocolError::Overflow("Logarithmic function: argument for log is too big (max should be u64::MAX)")); } - let argument = (*m as f64) * (diff as f64) / (*n as f64); + let argument = if *m == 1 { + if *n == 1 { + diff as f64 + } else { + (diff as f64) / (*n as f64) + } + } else if *n == 1 { + (*m as f64) * (diff as f64) + } else { + (*m as f64) * (diff as f64) / (*n as f64) + }; let log_val = argument.ln(); - let value = ((*a as f64) * log_val / (*d as f64)) + (*b as f64); - if let Some(max_value) = max_value { - if value.is_infinite() && value.is_sign_positive() || value > *max_value as f64 - { - return Ok(*max_value); - } - } - if !value.is_finite() || value > (u64::MAX as f64) { + + // Ensure the computed value is finite and within the u64 range. + if !log_val.is_finite() || log_val > (u64::MAX as f64) { return Err(ProtocolError::Overflow( - "Logarithmic function evaluation overflow or negative", + "InvertedLogarithmic: evaluation overflow", )); } - if value < 0.0 { + + let intermediate = if *a == 1 { + log_val + } else if *a == -1 { + -log_val + } else { + (*a as f64) * log_val + }; + + let value = if d == &1 { + if !intermediate.is_finite() || intermediate > (i64::MAX as f64) { + if let Some(max_value) = max_value { + if intermediate.is_sign_positive() { + *max_value as i64 + } else { + return Err(ProtocolError::Overflow( + "InvertedLogarithmic: evaluation overflow intermediate bigger than i64::max", + )); + } + } else { + return Err(ProtocolError::Overflow( + "InvertedLogarithmic: evaluation overflow intermediate bigger than i64::max", + )); + } + } else { + (intermediate.floor() as i64) + .checked_add(*b as i64) + .or(max_value.map(|max| max as i64)) + .ok_or(ProtocolError::Overflow( + "InvertedLogarithmic: evaluation overflow when adding b", + ))? + } + } else { + if !intermediate.is_finite() || intermediate > (i64::MAX as f64) { + return Err(ProtocolError::Overflow( + "InvertedLogarithmic: evaluation overflow intermediate bigger than i64::max", + )); + } + ((intermediate / (*d as f64)).floor() as i64) + .checked_add(*b as i64) + .or(max_value.map(|max| max as i64)) + .ok_or(ProtocolError::Overflow( + "InvertedLogarithmic: evaluation overflow when adding b", + ))? + }; + + if value < 0 { return if let Some(min_value) = min_value { Ok(*min_value) } else { @@ -374,6 +425,12 @@ impl DistributionFunction { return Ok(*min_value); } } + + if let Some(max_value) = max_value { + if value_u64 > *max_value { + return Ok(*max_value); + } + } Ok(value_u64) } DistributionFunction::InvertedLogarithmic { @@ -900,7 +957,7 @@ mod tests { n: 1, o: 0, start_moment: Some(0), - c: 10, + b: 10, min_value: None, max_value: None, }; @@ -918,7 +975,7 @@ mod tests { n: 1, o: 0, start_moment: Some(0), - c: 10, + b: 10, min_value: None, max_value: None, }; @@ -938,7 +995,7 @@ mod tests { n: 1, o: 0, start_moment: Some(0), - c: 5, + b: 5, min_value: None, max_value: None, }; @@ -957,7 +1014,7 @@ mod tests { n: 10, o: 0, start_moment: Some(0), - c: 0, + b: 0, min_value: None, max_value: None, }; @@ -976,7 +1033,7 @@ mod tests { n: 1, o: 0, start_moment: Some(0), - c: 0, + b: 0, min_value: None, max_value: Some(100000000), }; @@ -997,7 +1054,7 @@ mod tests { n: 1, o: 0, start_moment: Some(0), - c: 10, + b: 10, min_value: None, max_value: None, }; @@ -1016,7 +1073,7 @@ mod tests { n: 1, o: 0, start_moment: Some(0), - c: 10, + b: 10, min_value: Some(11), max_value: None, }; @@ -1035,7 +1092,7 @@ mod tests { n: 2, o: 0, start_moment: Some(0), - c: 10, + b: 10, min_value: Some(1), max_value: Some(11), // Set max at the starting value }; @@ -1061,7 +1118,7 @@ mod tests { n: 10, o: 0, start_moment: Some(0), - c: 5, + b: 5, min_value: None, max_value: None, }; diff --git a/packages/rs-dpp/src/data_contract/associated_token/token_perpetual_distribution/distribution_function/mod.rs b/packages/rs-dpp/src/data_contract/associated_token/token_perpetual_distribution/distribution_function/mod.rs index 6f59b4d855d..e2e70b3b345 100644 --- a/packages/rs-dpp/src/data_contract/associated_token/token_perpetual_distribution/distribution_function/mod.rs +++ b/packages/rs-dpp/src/data_contract/associated_token/token_perpetual_distribution/distribution_function/mod.rs @@ -355,7 +355,7 @@ pub enum DistributionFunction { /// The emission at period `x` is given by: /// /// ```text - /// f(x) = (a * e^(m * (x - s) / n)) / d + c + /// f(x) = (a * e^(m * (x - s) / n)) / d + b /// ``` /// /// # Parameters @@ -364,7 +364,7 @@ pub enum DistributionFunction { /// - `d`: A divisor used to scale the exponential term. /// - `s`: Optional start period offset. If not set, the contract creation start is assumed. /// - `o`: An offset for the exp function, this is useful if s is in None. - /// - `c`: An offset added to the result. + /// - `b`: An offset added to the result. /// - `min_value` / `max_value`: Optional constraints on the emitted tokens. /// /// # Use Cases @@ -390,7 +390,7 @@ pub enum DistributionFunction { /// /// ## **Example 2: Exponential Decay (`m < 0`)** /// - **Use Case**: A deflationary model where emissions start high and gradually decrease to ensure scarcity. - /// - **Parameters**: `a = 500`, `m = -3`, `n = 100`, `d = 20`, `c = 10` + /// - **Parameters**: `a = 500`, `m = -3`, `n = 100`, `d = 20`, `b = 10` /// - **Formula**: /// ```text /// f(x) = (500 * e^(-3 * (x - s) / 100)) / 20 + 10 @@ -403,12 +403,12 @@ pub enum DistributionFunction { n: u64, o: i64, start_moment: Option, - c: TokenAmount, + b: TokenAmount, min_value: Option, max_value: Option, }, - /// Emits tokens following a logarithmic function. + /// Emits tokens following a natural logarithmic (ln) function. /// /// # Formula /// The emission at period `x` is computed as: @@ -440,7 +440,7 @@ pub enum DistributionFunction { /// /// - Given the formula: /// ```text - /// f(x) = (a * log(m * (x - s + o) / n)) / d + b + /// f(x) = (a * ln(m * (x - s + o) / n)) / d + b /// ``` /// /// - Let’s assume the following parameters: @@ -452,7 +452,7 @@ pub enum DistributionFunction { /// /// - This results in: /// ```text - /// f(x) = (100 * log(2 * (x + 1) / 1)) / 10 + 50 + /// f(x) = (100 * ln(2 * (x + 1) / 1)) / 10 + 50 /// ``` /// /// - **Expected Behavior:** @@ -476,13 +476,13 @@ pub enum DistributionFunction { min_value: Option, max_value: Option, }, - /// Emits tokens following an inverted logarithmic function. + /// Emits tokens following an inverted natural logarithmic function. /// /// # Formula /// The emission at period `x` is given by: /// /// ```text - /// f(x) = (a * log( n / (m * (x - s + o)) )) / d + b + /// f(x) = (a * ln( n / (m * (x - s + o)) )) / d + b /// ``` /// /// # Parameters @@ -504,7 +504,7 @@ pub enum DistributionFunction { /// /// # Example /// ```text - /// f(x) = 10000 * log(5000 / x) + /// f(x) = 10000 * ln(5000 / x) /// ``` /// - Values: a = 10000 n = 5000 m = 1 o = 0 b = 0 d = 0 /// y @@ -651,18 +651,18 @@ impl fmt::Display for DistributionFunction { m, n, o, - start_moment: s, - c, + start_moment, + b, min_value, max_value, } => { write!(f, "Exponential: f(x) = {} * e^( {} * (x", a, m)?; - if let Some(start) = s { + if let Some(start) = start_moment { write!(f, " - {} + {})", start, o)?; } else { write!(f, " + {})", o)?; } - write!(f, " / {} ) / {} + {}", n, d, c)?; + write!(f, " / {} ) / {} + {}", n, d, b)?; if let Some(min) = min_value { write!(f, ", min: {}", min)?; } diff --git a/packages/rs-dpp/src/data_contract/associated_token/token_perpetual_distribution/distribution_function/validation.rs b/packages/rs-dpp/src/data_contract/associated_token/token_perpetual_distribution/distribution_function/validation.rs index 95023f2e647..ae87ad0c97e 100644 --- a/packages/rs-dpp/src/data_contract/associated_token/token_perpetual_distribution/distribution_function/validation.rs +++ b/packages/rs-dpp/src/data_contract/associated_token/token_perpetual_distribution/distribution_function/validation.rs @@ -428,7 +428,7 @@ impl DistributionFunction { } } } - // f(x) = (a * e^(m * (x - s + o) / n)) / d + c + // f(x) = (a * e^(m * (x - s + o) / n)) / d + b DistributionFunction::Exponential { a, d, @@ -436,7 +436,7 @@ impl DistributionFunction { n, o, start_moment: s, - c, + b, min_value, max_value, } => { @@ -514,10 +514,10 @@ impl DistributionFunction { )); } - if *a > MAX_DISTRIBUTION_PARAM { + if *b > MAX_DISTRIBUTION_PARAM { return Ok(SimpleConsensusValidationResult::new_with_error( InvalidTokenDistributionFunctionInvalidParameterError::new( - "a".to_string(), + "b".to_string(), 0, MAX_DISTRIBUTION_PARAM as i64, None, @@ -572,7 +572,7 @@ impl DistributionFunction { n: *n, o: *o, start_moment: Some(s.unwrap_or(start_moment)), - c: *c, + b: *b, min_value: *min_value, max_value: *max_value, } @@ -607,7 +607,7 @@ impl DistributionFunction { start_token_amount }; } - // f(x) = (a * log(m * (x - s + o) / n)) / d + b + // f(x) = (a * ln(m * (x - s + o) / n)) / d + b DistributionFunction::Logarithmic { a, d, @@ -629,7 +629,7 @@ impl DistributionFunction { InvalidTokenDistributionFunctionDivideByZeroError::new(self.clone()).into(), )); } - if *m == 0 { + if *m == 0 || *m > MAX_DISTRIBUTION_PARAM { return Ok(SimpleConsensusValidationResult::new_with_error( InvalidTokenDistributionFunctionInvalidParameterError::new( "m".to_string(), @@ -653,6 +653,18 @@ impl DistributionFunction { )); } + if *b > MAX_DISTRIBUTION_PARAM { + return Ok(SimpleConsensusValidationResult::new_with_error( + InvalidTokenDistributionFunctionInvalidParameterError::new( + "b".to_string(), + 0, + MAX_DISTRIBUTION_PARAM as i64, + None, + ) + .into(), + )); + } + if let Some(s) = s { if *s > MAX_DISTRIBUTION_PARAM { return Ok(SimpleConsensusValidationResult::new_with_error( @@ -779,6 +791,19 @@ impl DistributionFunction { .into(), )); } + + if *b > MAX_DISTRIBUTION_PARAM { + return Ok(SimpleConsensusValidationResult::new_with_error( + InvalidTokenDistributionFunctionInvalidParameterError::new( + "b".to_string(), + 0, + MAX_DISTRIBUTION_PARAM as i64, + None, + ) + .into(), + )); + } + // Check for division by zero. if *d == 0 { return Ok(SimpleConsensusValidationResult::new_with_error( @@ -1622,7 +1647,7 @@ mod tests { n: 2, o: -3999, start_moment: Some(0), - c: 10, + b: 10, min_value: Some(1), max_value: Some(1000000), }; @@ -1647,7 +1672,7 @@ mod tests { n: 0, o: 1, start_moment: Some(0), - c: 10, + b: 10, min_value: Some(1), max_value: Some(100), }; @@ -1667,7 +1692,7 @@ mod tests { n: 2, o: 1, start_moment: Some(0), - c: 10, + b: 10, min_value: Some(1), max_value: Some(100), }; @@ -1690,7 +1715,7 @@ mod tests { n: 2, o: 1, start_moment: Some(0), - c: 10, + b: 10, min_value: Some(1), max_value: Some(100), }; @@ -1713,7 +1738,7 @@ mod tests { n: 2, o: 1, start_moment: Some(0), - c: 10, + b: 10, min_value: Some(1), max_value: None, // Invalid: max_value must be set }; @@ -1736,7 +1761,7 @@ mod tests { n: 2, o: MAX_DISTRIBUTION_PARAM as i64 + 1, // Invalid: `o` exceeds allowed range start_moment: Some(0), - c: 10, + b: 10, min_value: Some(1), max_value: Some(100), }; @@ -1759,7 +1784,7 @@ mod tests { n: 2, o: 1, start_moment: Some(0), - c: 10, + b: 10, min_value: Some(50), // Invalid: min > max max_value: Some(30), }; @@ -1782,7 +1807,7 @@ mod tests { n: 4, o: 2, start_moment: Some(START_MOMENT), - c: 8, + b: 8, min_value: Some(2), max_value: Some(50), }; @@ -1802,7 +1827,7 @@ mod tests { n: 4, o: 1, start_moment: Some(START_MOMENT), - c: 8, + b: 8, min_value: Some(2), max_value: Some(MAX_DISTRIBUTION_PARAM), // Valid max }; @@ -1822,7 +1847,7 @@ mod tests { n: 1, o: 1, start_moment: Some(0), - c: 10, + b: 10, min_value: Some(1), max_value: Some(MAX_DISTRIBUTION_PARAM), }; @@ -1845,7 +1870,7 @@ mod tests { n: 1, o: 0, start_moment: Some(0), - c: 10, + b: 10, min_value: Some(1), max_value: Some(1000), // Small `max_value` }; @@ -1868,7 +1893,7 @@ mod tests { n: 2, o: 0, start_moment: Some(0), - c: 10, + b: 10, min_value: Some(10), // Function starts at `min_value` max_value: Some(1000), }; @@ -1891,7 +1916,7 @@ mod tests { n: 2, o: 1, start_moment: Some(0), - c: 5, + b: 5, min_value: Some(1), max_value: None, // Should fail }; @@ -1914,7 +1939,7 @@ mod tests { n: 1, o: i64::MAX / 2, // Large `o` start_moment: Some(0), - c: 5, + b: 5, min_value: Some(1), max_value: Some(MAX_DISTRIBUTION_PARAM), }; @@ -1937,7 +1962,7 @@ mod tests { n: 2, o: 0, start_moment: Some(0), - c: 10, + b: 10, min_value: Some(10), max_value: Some(100), }; @@ -1960,7 +1985,7 @@ mod tests { n: 10, o: -3, start_moment: Some(0), - c: 5, + b: 5, min_value: Some(10), max_value: Some(1000), }; @@ -1991,7 +2016,7 @@ mod tests { n: 4, o: 2, start_moment: Some(START_MOMENT), - c: 8, + b: 8, min_value: Some(5), max_value: Some(100), }; @@ -2011,7 +2036,7 @@ mod tests { n: 3, o: 5, // Shift start start_moment: Some(START_MOMENT), - c: 10, + b: 10, min_value: Some(5), max_value: Some(100), }; @@ -2090,6 +2115,29 @@ mod tests { ); } + #[test] + fn test_logarithmic_invalid_zero_m() { + let dist = DistributionFunction::Logarithmic { + a: 4, + d: 10, + m: 0, // Invalid: this would make it a constant + n: 1, + o: 1, + start_moment: Some(0), + b: 10, + min_value: Some(1), + max_value: Some(100), + }; + let result = dist.validate(START_MOMENT); + assert!( + result + .expect("no error on test_logarithmic_invalid_zero_m") + .first_error() + .is_some(), + "Expected m == 0 error" + ); + } + #[test] fn test_logarithmic_invalid_x_s_o_non_positive() { let dist = DistributionFunction::Logarithmic { diff --git a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/token/distribution/perpetual/block_based.rs b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/token/distribution/perpetual/block_based.rs index 7d7c0689ae5..75c6a982b86 100644 --- a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/token/distribution/perpetual/block_based.rs +++ b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/token/distribution/perpetual/block_based.rs @@ -2058,10 +2058,10 @@ mod logarithmic { use super::test_suite::check_heights; use dpp::data_contract::associated_token::token_perpetual_distribution::distribution_function::DistributionFunction::{self,Logarithmic}; + use dpp::data_contract::associated_token::token_perpetual_distribution::distribution_function::{MAX_DISTRIBUTION_PARAM, MAX_LOG_A_PARAM, MIN_LOG_A_PARAM}; - /// "fails: ones - use of ln instead of log as documented #[test] - fn fails_ones() -> Result<(), String> { + fn log_distribution_basic() -> Result<(), String> { test_logarithmic( Logarithmic { a: 1, // a: i64, @@ -2075,111 +2075,60 @@ mod logarithmic { max_value: None, // max_value: Option, }, &[ - (1, 100_001, true), // log(0)+1 = 1 - (2, 100_002, true), // log(1)+1 = 1 - (3, 100_003, true), // log(3)+1 = 1 - (4, 100_005, true), // log(4)+1 = 2 (log(4) == 0.6, rounded up to 1) + (1, 100_001, true), // ln(0)+1 = 1 + (2, 100_002, true), // ln(1)+1 = 1 + (3, 100_004, true), // ln(3)+1 = 2 + (4, 100_006, true), // ln(4)+1 = 2 ], 1, ) } + #[test] - fn fails_div_by_0() -> Result<(), String> { + fn log_distribution_1_div_u64_max() -> Result<(), String> { + // n is very big here, so we would expect to get 0 test_logarithmic( Logarithmic { a: 1, // a: i64, d: 1, // d: u64, m: 1, // m: u64, - n: 0, // n: u64, - o: 1, // o: i64, - start_moment: Some(1), // start_moment: Option, - b: 1, // b: TokenAmount, - min_value: None, // min_value: Option, - max_value: None, // max_value: Option, - }, - &[(2, 100_002, false)], - 1, - ) - } - #[test] - fn fails_log_0() -> Result<(), String> { - test_logarithmic( - Logarithmic { - a: 1, // a: i64, - d: 1, // d: u64, - m: 0, // m: u64, - n: 1, // n: u64, - o: 1, // o: i64, - start_moment: Some(1), // start_moment: Option, - b: 1, // b: TokenAmount, + n: u64::MAX, // n: u64, + o: 0, // o: i64, + start_moment: Some(0), // start_moment: Option, + b: 0, // b: TokenAmount, min_value: None, // min_value: Option, max_value: None, // max_value: Option, }, - &[(1, 100_001, true), (5, 100_001, true)], + &[(1, 100_000, false), (5, 100_000, false)], 1, ) } - /// min == max means linear #[test] - fn min_eq_max() -> Result<(), String> { + fn log_distribution_neg_1_div_u64_max() -> Result<(), String> { + // n is very big here, so we would expect to get 0 test_logarithmic( Logarithmic { - a: 1, // a: i64, + a: -1, // a: i64, d: 1, // d: u64, m: 1, // m: u64, - n: 1, // n: u64, - o: 1, // o: i64, - start_moment: Some(1), // start_moment: Option, + n: u64::MAX, // n: u64, + o: 0, // o: i64, + start_moment: Some(0), // start_moment: Option, b: 0, // b: TokenAmount, - min_value: Some(10), // min_value: Option, - max_value: Some(10), // max_value: Option, + min_value: None, // min_value: Option, + max_value: None, // max_value: Option, }, - &[(1, 100_010, true), (5, 100_050, true)], + &[(1, 100_044, true), (5, 100_214, true)], 1, ) } + #[test] - fn min_eq_max_interval_5() -> Result<(), String> { - test_logarithmic( - Logarithmic { - a: 1, // a: i64, - d: 1, // d: u64, - m: 1, // m: u64, - n: 1, // n: u64, - o: 1, // o: i64, - start_moment: Some(1), // start_moment: Option, - b: 0, // b: TokenAmount, - min_value: Some(10), // min_value: Option, - max_value: Some(10), // max_value: Option, - }, - &[(5, 100_010, true), (10, 100_020, true)], - 5, - ) - } - #[test] - fn fails_min_gt_max() -> Result<(), String> { - test_logarithmic( - Logarithmic { - a: 1, // a: i64, - d: 1, // d: u64, - m: 1, // m: u64, - n: 1, // n: u64, - o: 1, // o: i64, - start_moment: Some(1), // start_moment: Option, - b: 1, // b: TokenAmount, - min_value: Some(10), // min_value: Option, - max_value: Some(5), // max_value: Option, - }, - &[(5, 100_000, false), (10, 100_000, false)], - 5, - ) - } - #[test] - fn fails_a_min() -> Result<(), String> { + fn log_distribution_a_min() -> Result<(), String> { test_logarithmic( Logarithmic { - a: i64::MIN, // a: i64, + a: MIN_LOG_A_PARAM, // a: i64, d: 1, // d: u64, m: 1, // m: u64, n: 1, // n: u64, @@ -2191,125 +2140,58 @@ mod logarithmic { }, // f(x) = (a * log(m * (x - s + o) / n)) / d + b &[ - (1, 100_000, false), // should be false, as the balance after claim == initial balance - (2, 100_001, true), + (1, 100_001, true), + (2, 100_001, false), (9, 100_001, false), (10, 100_001, false), ], 1, ) } - /// Given a logarithmic distribution function with a=MAX, - /// When I try to claim tokens, - /// Then I get an error different from InternalError. - /// - /// - #[test] - fn fails_a_max_overflows() -> Result<(), String> { - test_logarithmic( - Logarithmic { - a: i64::MAX, // a: i64, - d: 1, // d: u64, - m: 1, // m: u64, - n: 1, // n: u64, - o: 1, // o: i64, - start_moment: Some(1), // start_moment: Option, - b: 1, // b: TokenAmount, - min_value: None, // min_value: Option, - max_value: None, // max_value: Option, - }, - &[ - (1, 100_000, false), - (9, 100_000, false), - (10, 100_000, false), - ], - 1, - ) - } - #[test] - fn a_0_b_0() -> Result<(), String> { - test_logarithmic( - Logarithmic { - a: 0, // a: i64, - d: 1, // d: u64, - m: 1, // m: u64, - n: 1, // n: u64, - o: 1, // o: i64, - start_moment: Some(1), // start_moment: Option, - b: 0, // b: TokenAmount, - min_value: None, // min_value: Option, - max_value: None, // max_value: Option, - }, - &[ - (1, 100_000, false), - (9, 100_000, false), - (10, 100_000, false), - ], - 1, - ) - } - #[test] - fn fails_log_negative() -> Result<(), String> { - test_logarithmic( - Logarithmic { - a: 1, // a: i64, - d: 1, // d: u64, - m: 1, // m: u64, - n: 1, // n: u64, - o: -10, // o: i64, - start_moment: Some(1), // start_moment: Option, - b: 0, // b: TokenAmount, - min_value: None, // min_value: Option, - max_value: None, // max_value: Option, - }, - &[ - (1, 100_000, false), - (9, 100_000, false), - (10, 100_000, false), - ], - 1, - ) - } + #[test] - fn fails_o_min() -> Result<(), String> { + fn log_distribution_max_amounts() { test_logarithmic( Logarithmic { - a: 1, // a: i64, - d: 1, // d: u64, - m: 1, // m: u64, - n: 1, // n: u64, - o: i64::MIN, // o: i64, - start_moment: Some(1), // start_moment: Option, - b: 0, // b: TokenAmount, - min_value: None, // min_value: Option, - max_value: None, // max_value: Option, + a: MAX_LOG_A_PARAM, // a: i64, + d: 1, // d: u64, + m: MAX_DISTRIBUTION_PARAM, // m: u64, + n: 1, // n: u64, + o: MAX_DISTRIBUTION_PARAM as i64, // o: i64, + start_moment: Some(0), // start_moment: Option, + b: MAX_DISTRIBUTION_PARAM, // b: TokenAmount, + min_value: None, // min_value: Option, + max_value: None, // max_value: Option, }, &[ - (1, 100_000, false), - (9, 100_000, false), - (10, 100_000, false), + (1, 281474978991040, true), + (9, 2533274810119360, true), + (10, 2814749789010400, true), + (200, 38843547087063520, true), ], 1, ) + .expect("expect to pass"); } + #[test] - fn fails_b_max() -> Result<(), String> { + fn log_distribution_with_b_max() -> Result<(), String> { test_logarithmic( Logarithmic { - a: 1, // a: i64, - d: 1, // d: u64, - m: 1, // m: u64, - n: 1, // n: u64, - o: 1, // o: i64, - start_moment: Some(1), // start_moment: Option, - b: u64::MAX, // b: TokenAmount, - min_value: None, // min_value: Option, - max_value: None, // max_value: Option, + a: 1, // a: i64, + d: 1, // d: u64, + m: 1, // m: u64, + n: 1, // n: u64, + o: 1, // o: i64, + start_moment: Some(1), // start_moment: Option, + b: MAX_DISTRIBUTION_PARAM, // b: TokenAmount, + min_value: None, // min_value: Option, + max_value: None, // max_value: Option, }, &[ - (1, 100_000, false), - (9, 100_000, false), - (10, 100_000, false), + (1, 281474976810655, true), // We start at 1 + (9, 2533274790495904, true), + (10, 2814749767206561, true), ], 1, ) @@ -2338,7 +2220,7 @@ mod inverted_logarithmic { use dpp::data_contract::associated_token::token_perpetual_distribution::distribution_function::DistributionFunction::{self,InvertedLogarithmic}; #[test] - fn ones() -> Result<(), String> { + fn inv_log_distribution_very_low_emission() -> Result<(), String> { // At block 2 no more can ever be claimed because the function is decreasing let dist = InvertedLogarithmic { a: 1, // a: i64, @@ -2364,7 +2246,7 @@ mod inverted_logarithmic { } #[test] - fn inv_log_reduced_emission() -> Result<(), String> { + fn inv_log_distribution_reduced_emission() -> Result<(), String> { // y // ↑ // 10000 |* @@ -2412,7 +2294,7 @@ mod inverted_logarithmic { } #[test] - fn inv_log_reduced_emission_passing_0() -> Result<(), String> { + fn inv_log_distribution_reduced_emission_passing_0() -> Result<(), String> { // y // ↑ // 350 |* @@ -2447,7 +2329,7 @@ mod inverted_logarithmic { } #[test] - fn inv_log_negative_a_increase_emission() -> Result<(), String> { + fn inv_log_distribution_negative_a_increase_emission() -> Result<(), String> { // y // ↑ // 10000 |