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 e0013a0a34b..f88a3faf8c0 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 @@ -420,7 +420,11 @@ impl DistributionFunction { } // Calculate the denominator for the logarithm: m * (x - s + o) - let denom_f = (*m as f64) * (diff as f64); + let denom_f = if *m == 1 { + diff as f64 + } else { + (*m as f64) * (diff as f64) + }; if denom_f <= 0.0 { return Err(ProtocolError::Overflow( "InvertedLogarithmic: computed denominator is non-positive", @@ -437,26 +441,48 @@ impl DistributionFunction { let log_val = argument.ln(); - // Compute the final value: (a * ln(...)) / d + b. - let value = ((*a as f64) * log_val / (*d as f64)) + (*b 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( + "InvertedLogarithmic: evaluation overflow", + )); + } + + let intermediate = if *a == 1 { + log_val + } else if *a == -1 { + -log_val + } else { + (*a as f64) * log_val + }; + if !intermediate.is_finite() || intermediate > (i64::MAX as f64) { + return Err(ProtocolError::Overflow( + "InvertedLogarithmic: evaluation overflow intermediate bigger than i64::max", + )); + } + + let value = if d == &1 { + (intermediate.floor() as i64).checked_add(*b as i64).ok_or( + ProtocolError::Overflow( + "InvertedLogarithmic: evaluation overflow when adding b", + ), + )? + } else { + ((intermediate / (*d as f64)).floor() as i64) + .checked_add(*b as i64) + .ok_or(ProtocolError::Overflow( + "InvertedLogarithmic: evaluation overflow when adding b", + ))? + }; // Clamp to max_value if provided. if let Some(max_value) = max_value { - if value > *max_value as f64 - || (value.is_infinite() && value.is_sign_positive()) - { + if value > *max_value as i64 { return Ok(*max_value); } } - // Ensure the computed value is finite and within the u64 range. - if !value.is_finite() || value > (u64::MAX as f64) { - return Err(ProtocolError::Overflow( - "InvertedLogarithmic: evaluation overflow", - )); - } - - if value < 0.0 { + if value < 0 { return if let Some(min_value) = min_value { Ok(*min_value) } else { 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 a7762b72fe8..6f59b4d855d 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 @@ -22,6 +22,10 @@ pub const DEFAULT_STEP_DECREASING_AMOUNT_MAX_CYCLES_BEFORE_TRAILING_DISTRIBUTION pub const MAX_LINEAR_SLOPE_PARAM: u64 = 256; +pub const MIN_LOG_A_PARAM: i64 = -32_766; +pub const MAX_LOG_A_PARAM: i64 = 32_767; +pub const MAX_EXP_A_PARAM: u64 = 256; + #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, PartialOrd)] pub enum DistributionFunction { /// Emits a constant (fixed) number of tokens for every period. @@ -499,22 +503,24 @@ pub enum DistributionFunction { /// claimants receive diminishing rewards. /// /// # Example - /// - Suppose a system starts with **500 tokens per period** and gradually reduces over time: - /// /// ```text - /// f(x) = (1000 * log(5000 / (5 * (x - 1000)))) / 10 + 10 + /// f(x) = 10000 * log(5000 / x) /// ``` - /// - /// Example values: - /// - /// | Period (x) | Emission (f(x)) | - /// |------------|----------------| - /// | 1000 | 500 tokens | - /// | 1500 | 230 tokens | - /// | 2000 | 150 tokens | - /// | 5000 | 50 tokens | - /// | 10,000 | 20 tokens | - /// | 50,000 | 10 tokens | + /// - Values: a = 10000 n = 5000 m = 1 o = 0 b = 0 d = 0 + /// y + /// ↑ + /// 10000 |* + /// 9000 | * + /// 8000 | * + /// 7000 | * + /// 6000 | * + /// 5000 | * + /// 4000 | * + /// 3000 | * + /// 2000 | * + /// 1000 | * + /// 0 +-------------------*----------→ x + /// 0 2000 4000 6000 8000 /// /// - The emission **starts high** and **gradually decreases**, ensuring early adopters receive /// more tokens while later participants still get rewards. 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 1af60a59758..95023f2e647 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 @@ -5,7 +5,8 @@ use crate::consensus::basic::data_contract::{ InvalidTokenDistributionFunctionInvalidParameterTupleError, }; use crate::data_contract::associated_token::token_perpetual_distribution::distribution_function::{ - DistributionFunction, MAX_DISTRIBUTION_PARAM, MAX_LINEAR_SLOPE_PARAM, + DistributionFunction, MAX_DISTRIBUTION_PARAM, MAX_EXP_A_PARAM, MAX_LINEAR_SLOPE_PARAM, + MAX_LOG_A_PARAM, MIN_LOG_A_PARAM, }; use crate::validation::SimpleConsensusValidationResult; use crate::ProtocolError; @@ -60,7 +61,7 @@ impl DistributionFunction { step_count, decrease_per_interval_numerator, decrease_per_interval_denominator, - start_decreasing_offset: s, + start_decreasing_offset, max_interval_count, distribution_start_amount, trailing_distribution_interval_amount, @@ -68,7 +69,7 @@ impl DistributionFunction { } => { // Validate n. if *distribution_start_amount == 0 - || *distribution_start_amount > MAX_DISTRIBUTION_PARAM as u64 + || *distribution_start_amount > MAX_DISTRIBUTION_PARAM { return Ok(SimpleConsensusValidationResult::new_with_error( InvalidTokenDistributionFunctionInvalidParameterError::new( @@ -149,7 +150,7 @@ impl DistributionFunction { } } - if let Some(s) = s { + if let Some(s) = start_decreasing_offset { if *s > MAX_DISTRIBUTION_PARAM { return Ok(SimpleConsensusValidationResult::new_with_error( InvalidTokenDistributionFunctionInvalidParameterError::new( @@ -460,12 +461,13 @@ impl DistributionFunction { .into(), )); } - if *a == 0 { + // Check valid a values + if *a == 0 || *a > MAX_EXP_A_PARAM { return Ok(SimpleConsensusValidationResult::new_with_error( InvalidTokenDistributionFunctionInvalidParameterError::new( "a".to_string(), 1, - MAX_DISTRIBUTION_PARAM as i64, + MAX_LOG_A_PARAM, None, ) .into(), @@ -638,13 +640,14 @@ impl DistributionFunction { .into(), )); } - if *a == 0 { + // Check valid a values + if *a == 0 || *a < MIN_LOG_A_PARAM || *a > MAX_LOG_A_PARAM { return Ok(SimpleConsensusValidationResult::new_with_error( InvalidTokenDistributionFunctionInvalidParameterError::new( "a".to_string(), - 1, - MAX_DISTRIBUTION_PARAM as i64, - None, + MIN_LOG_A_PARAM, + MAX_LOG_A_PARAM, + Some(0), ) .into(), )); @@ -764,6 +767,18 @@ impl DistributionFunction { min_value, max_value, } => { + // Check valid a values + if *a == 0 || *a < MIN_LOG_A_PARAM || *a > MAX_LOG_A_PARAM { + return Ok(SimpleConsensusValidationResult::new_with_error( + InvalidTokenDistributionFunctionInvalidParameterError::new( + "a".to_string(), + MIN_LOG_A_PARAM, + MAX_LOG_A_PARAM, + Some(0), + ) + .into(), + )); + } // Check for division by zero. if *d == 0 { return Ok(SimpleConsensusValidationResult::new_with_error( @@ -2209,6 +2224,66 @@ mod tests { ); } + #[test] + fn test_inverted_logarithmic_invalid_zero_a() { + let dist = DistributionFunction::InvertedLogarithmic { + a: 0, + d: 1, + m: 1, + n: 100, + o: 1, + start_moment: Some(0), + b: 5, + min_value: Some(1), + max_value: Some(MAX_DISTRIBUTION_PARAM), // Valid max boundary + }; + let result = dist.validate(START_MOMENT); + assert_eq!( + result.expect("expected valid").first_error().expect("expected error").to_string(), + "Invalid parameter `a` in token distribution function. Expected range: -32766 to 32767 except 0 (which we got)" + ); + } + + #[test] + fn test_inverted_logarithmic_invalid_too_low_a() { + let dist = DistributionFunction::InvertedLogarithmic { + a: -50000, + d: 1, + m: 1, + n: 100, + o: 1, + start_moment: Some(0), + b: 5, + min_value: Some(1), + max_value: Some(MAX_DISTRIBUTION_PARAM), // Valid max boundary + }; + let result = dist.validate(START_MOMENT); + assert_eq!( + result.expect("expected valid").first_error().expect("expected error").to_string(), + "Invalid parameter `a` in token distribution function. Expected range: -32766 to 32767 except 0 (which we got)" + ); + } + + #[test] + fn test_inverted_logarithmic_invalid_too_high_a() { + let dist = DistributionFunction::InvertedLogarithmic { + a: 50000, + d: 1, + m: 1, + n: 100, + o: 1, + start_moment: Some(0), + b: 5, + min_value: Some(1), + max_value: Some(MAX_DISTRIBUTION_PARAM), // Valid max boundary + }; + let result = dist.validate(START_MOMENT); + assert_eq!( + result.expect("expected valid").first_error().expect("expected error").to_string(), + "Invalid parameter `a` in token distribution function. Expected range: -32766 to 32767 except 0 (which we got)" + ); + } + #[test] fn test_inverted_logarithmic_invalid_divide_by_zero_d() { let dist = DistributionFunction::InvertedLogarithmic { @@ -2349,6 +2424,28 @@ mod tests { ); } + #[test] + fn test_inverted_logarithmic_valid_with_min_a() { + // Since `a` is negative, the inverted logarithmic function is increasing, + // but it starts at the maximum value already, so it will never produce a higher value. + let dist = DistributionFunction::InvertedLogarithmic { + a: i64::MIN, + d: 1, + m: 1, + n: 100, + o: 1, + start_moment: Some(0), + b: 5, + min_value: Some(1), + max_value: Some(MAX_DISTRIBUTION_PARAM), // Valid max boundary + }; + let result = dist.validate(START_MOMENT); + assert_eq!( + result.expect("expected valid").first_error().expect("expected error").to_string(), + "Invalid parameter `a` in token distribution function. Expected range: -32766 to 32767 except 0 (which we got)" + ); + } + #[test] fn test_inverted_logarithmic_invalid_starting_at_max_for_increasing() { let dist = DistributionFunction::InvertedLogarithmic { diff --git a/packages/rs-dpp/src/errors/consensus/basic/data_contract/invalid_token_distribution_function_incoherence_error.rs b/packages/rs-dpp/src/errors/consensus/basic/data_contract/invalid_token_distribution_function_incoherence_error.rs index da87ea8a346..3224b76e8df 100644 --- a/packages/rs-dpp/src/errors/consensus/basic/data_contract/invalid_token_distribution_function_incoherence_error.rs +++ b/packages/rs-dpp/src/errors/consensus/basic/data_contract/invalid_token_distribution_function_incoherence_error.rs @@ -10,7 +10,7 @@ use bincode::{Decode, Encode}; #[derive( Error, Debug, Clone, PartialEq, Eq, Encode, Decode, PlatformSerialize, PlatformDeserialize, )] -#[error("Incoherent parameters in token distribution function: {}.", message)] +#[error("Incoherent parameters in token distribution function: {}", message)] #[platform_serialize(unversioned)] pub struct InvalidTokenDistributionFunctionIncoherenceError { /* 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 5e95a903ae2..7d7c0689ae5 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 @@ -173,7 +173,7 @@ mod perpetual_distribution_block { assert_matches!( processing_result.execution_results().as_slice(), - [StateTransitionExecutionResult::PaidConsensusError( + [PaidConsensusError( ConsensusError::StateError(StateError::InvalidTokenClaimNoCurrentRewards(_)), _ )] @@ -354,7 +354,7 @@ mod perpetual_distribution_block { assert_matches!( processing_result.execution_results().as_slice(), - [StateTransitionExecutionResult::PaidConsensusError( + [PaidConsensusError( ConsensusError::StateError(StateError::InvalidTokenClaimWrongClaimant(_)), _ )] @@ -816,7 +816,7 @@ mod random { suite.execute(&tests).expect("should execute"); let data = balances_result.lock().unwrap(); - // substract balance from previous step (for first step, substract initial balance of 100_000) + // subtract balance from previous step (for first step, subtract initial balance of 100_000) let diffs: Vec = data .iter() .scan(INITIAL_BALANCE, |prev, &x| { @@ -871,7 +871,7 @@ mod step_decreasing { use dpp::balances::credits::TokenAmount; use dpp::data_contract::associated_token::token_perpetual_distribution::distribution_function::{DistributionFunction, MAX_DISTRIBUTION_PARAM}; use dpp::prelude::{BlockHeight, BlockHeightInterval}; - use crate::{execution::validation::state_transition::batch::tests::token::distribution::perpetual::block_based::test_suite::check_heights, platform_types::state_transitions_processing_result::StateTransitionExecutionResult}; + use crate::{execution::validation::state_transition::batch::tests::token::distribution::perpetual::block_based::test_suite::check_heights}; use crate::execution::validation::state_transition::batch::tests::token::distribution::perpetual::block_based::INITIAL_BALANCE; const DECREASING_ONE_PERCENT_100K: [TokenAmount; 500] = [ @@ -1755,7 +1755,7 @@ mod polynomial { /// Divide by 0 /// claim at height 10: claim failed: assertion 1 failed: expected SuccessfulExecution, got - /// [InternalError(\"storage: protocol: divide by zero error: Polynomial function: divisor d is 0\")]\nexpected balance Some(1100055) but got 100000\n\n--> + /// [InternalError(\"storage: protocol: divide by zero error: Polynomial function: divisor d is 0\")]\n expected balance Some(1100055) but got 100000\n\n--> #[test] fn fails_divide_by_0() -> Result<(), String> { test_polynomial( @@ -1979,11 +1979,11 @@ mod polynomial { }) } - /// Test various combinations of `m/n` in [DistributionFunction::Polynomial] distribution. + /// Test various combinations of `m/n` in `[DistributionFunction::Polynomial]` distribution. /// /// We expect this test not to end with InternalError. #[test] - fn fails_poynomial_power() -> Result<(), String> { + fn fails_polynomial_power() -> Result<(), String> { for m in [i64::MIN, -1, 0, 1, i64::MAX] { for n in [0, 1, u64::MAX] { let dist = Polynomial { @@ -2059,25 +2059,6 @@ mod logarithmic { use super::test_suite::check_heights; use dpp::data_contract::associated_token::token_perpetual_distribution::distribution_function::DistributionFunction::{self,Logarithmic}; - #[test] - fn fails_zeros() -> Result<(), String> { - test_logarithmic( - Logarithmic { - a: 0, // a: i64, - d: 0, // d: u64, - m: 0, // m: u64, - n: 0, // 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, - }, - &[(4, 100_000, true)], - 1, - ) - } - /// "fails: ones - use of ln instead of log as documented #[test] fn fails_ones() -> Result<(), String> { @@ -2220,7 +2201,7 @@ mod logarithmic { } /// Given a logarithmic distribution function with a=MAX, /// When I try to claim tokens, - /// Then I get an error different than InternalError. + /// Then I get an error different from InternalError. /// /// #[test] @@ -2357,25 +2338,8 @@ mod inverted_logarithmic { use dpp::data_contract::associated_token::token_perpetual_distribution::distribution_function::DistributionFunction::{self,InvertedLogarithmic}; #[test] - fn fails_zeros() -> Result<(), String> { - let dist = InvertedLogarithmic { - a: 0, // a: i64, - d: 0, // d: u64, - m: 0, // m: u64, - n: 0, // 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, - }; - let steps = [(4, 100_000, true)]; - - run_test(dist, &steps, 1) - } - - #[test] - fn fails_ones() -> Result<(), String> { + fn ones() -> Result<(), String> { + // At block 2 no more can ever be claimed because the function is decreasing let dist = InvertedLogarithmic { a: 1, // a: i64, d: 1, // d: u64, @@ -2389,235 +2353,145 @@ mod inverted_logarithmic { }; let steps = [ (1, 100_001, true), - (2, 100_002, true), - (3, 100_003, true), - (4, 100_005, true), - ]; - - run_test(dist, &steps, 1) - } - - #[test] - fn fails_divide_by_zero() -> Result<(), String> { - let dist = InvertedLogarithmic { - a: 1, // a: i64, - d: 0, // 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, - }; - let steps = [(2, 100_002, false)]; - - run_test(dist, &steps, 1) - } - - #[test] - fn fails_n_zero_log_zero() -> Result<(), String> { - let dist = InvertedLogarithmic { - 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, - }; - let steps = [(1, 100_001, true), (5, 100_001, true)]; - - run_test(dist, &steps, 1) - } - - #[test] - fn min_eq_max_means_linear() -> Result<(), String> { - let dist = InvertedLogarithmic { - 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, - }; - let steps = [(1, 100_010, true), (5, 100_050, true)]; - - run_test(dist, &steps, 1) - } - - #[test] - fn min_eq_max_means_linear_interval_5() -> Result<(), String> { - let dist = InvertedLogarithmic { - 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, - }; - let steps = [(5, 100_010, true), (10, 100_020, true)]; - - run_test(dist, &steps, 5) - } - - #[test] - fn fails_min_gt_max() -> Result<(), String> { - let dist = InvertedLogarithmic { - 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, - }; - let steps = [(5, 100_000, false), (10, 100_000, false)]; - - run_test(dist, &steps, 5) - } - - #[test] - fn fails_a_min() -> Result<(), String> { - let dist = InvertedLogarithmic { - a: i64::MIN, // 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, - }; - let steps = [ - (1, 100_000, false), // f(1) should be < 0, is 1 - (9, 100_000, false), - (10, 100_000, false), + (2, 100_001, false), + (50000, 100_001, false), ]; - + let x_1 = dist.evaluate(0, 1).expect("expected to evaluate"); + assert_eq!(x_1, 1); // This is ln (1/ (1 - 1 + 1)), or basically ln(1) = 1 + let x_2 = dist.evaluate(0, 2).expect("expected to evaluate"); + assert_eq!(x_2, 0); // This is ln (1/ (1 - 1 + 2)), or basically ln(1/2) = 0 run_test(dist, &steps, 1) } #[test] - fn a_max() -> Result<(), String> { + fn inv_log_reduced_emission() -> Result<(), String> { + // y + // ↑ + // 10000 |* + // 9000 | * + // 8000 | * + // 7000 | * + // 6000 | * + // 5000 | * + // 4000 | * + // 3000 | * + // 2000 | * + // 1000 | * + // 0 +-------------------*----------→ x + // 0 2000 4000 6000 8000 let dist = InvertedLogarithmic { - a: i64::MAX, // a: i64, + a: 10000, // 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, - }; - let steps = [ - (1, 100_001, true), // f(x) = 0 for x>1 - (9, 100_001, false), - (10, 100_001, false), - ]; - - run_test(dist, &steps, 1) - } - - #[test] - fn a_zero_b_zero() -> Result<(), String> { - let dist = InvertedLogarithmic { - 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, + n: 5000, // 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, }; + let x_1 = dist.evaluate(0, 1).expect("expected to evaluate"); + let x_2 = dist.evaluate(0, 2).expect("expected to evaluate"); + let x_1000 = dist.evaluate(0, 1000).expect("expected to evaluate"); + let x_4000 = dist.evaluate(0, 4000).expect("expected to evaluate"); + let x_5000 = dist.evaluate(0, 5000).expect("expected to evaluate"); + let x_6000 = dist.evaluate(0, 6000).expect("expected to evaluate"); + assert_eq!(x_1, 85171); + assert_eq!(x_2, 78240); + assert_eq!(x_1000, 16094); + assert_eq!(x_4000, 2231); + assert_eq!(x_5000, 0); + assert_eq!(x_6000, 0); let steps = [ - (1, 100_000, false), - (9, 100_000, false), - (10, 100_000, false), + (1, 185_171, true), + (2, 263_411, true), + (1000, 6_110_958, true), ]; run_test(dist, &steps, 1) } #[test] - fn fails_log_negative() -> Result<(), String> { + fn inv_log_reduced_emission_passing_0() -> Result<(), String> { + // y + // ↑ + // 350 |* + // 300 | * + // 250 | * + // 200 | * + // 150 | * + // 100 | * + // 50 | * + // 0 +-------------*--------------→ x + // 0 100 200 300 400 let dist = InvertedLogarithmic { - a: 1, // a: i64, + a: 100, // 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, + n: 200, // 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, }; let steps = [ - (1, 100_000, false), - (9, 100_000, false), - (10, 100_000, false), + (1, 100529, true), + (2, 100989, true), + (100, 116559, true), + (210, 119546, true), + (300, 119546, false), // past 200 we won't get any more ]; run_test(dist, &steps, 1) } #[test] - fn fails_o_min() -> Result<(), String> { + fn inv_log_negative_a_increase_emission() -> Result<(), String> { + // y + // ↑ + // 10000 | + // 9000 | + // 8000 | + // 7000 | * + // 6000 | * + // 5000 | * + // 4000 | * + // 3000 | * + // 2000 | * + // 1000 * + // 0 +-------------------------------------------→ x + // 0 5k 10k 15k 20k 25k 30k let dist = InvertedLogarithmic { - a: 1, // a: i64, + a: -2200, // 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, + n: 10000, // n: u64, + o: 3000, // o: i64, + start_moment: Some(0), // start_moment: Option, + b: 4000, // b: TokenAmount, min_value: None, // min_value: Option, max_value: None, // max_value: Option, }; + let x_1 = dist.evaluate(0, 1).expect("expected to evaluate"); + let x_2 = dist.evaluate(0, 2).expect("expected to evaluate"); + let x_1000 = dist.evaluate(0, 1000).expect("expected to evaluate"); + let x_4000 = dist.evaluate(0, 4000).expect("expected to evaluate"); + assert_eq!(x_1, 1351); + assert_eq!(x_2, 1352); + assert_eq!(x_1000, 1984); + assert_eq!(x_4000, 3215); let steps = [ - (1, 100_000, false), - (9, 100_000, false), - (10, 100_000, false), + (1, 101351, true), + (2, 102703, true), + (100, 238739, true), + (210, 399539, true), + (300, 537282, true), ]; run_test(dist, &steps, 1) } - #[test] - fn fails_b_max() -> Result<(), String> { - let dist = InvertedLogarithmic { - 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, - }; - let steps = [ - (1, 100_000, false), - (9, 100_000, false), - (10, 100_000, false), - ]; - - run_test(dist, &steps, 1) - } /// f(x) = (a * log( n / (m * (x - s + o)) )) / d + b fn run_test( dist: DistributionFunction, @@ -2686,7 +2560,7 @@ mod test_suite { /// * `distribution_interval` - interval between distributions /// * `max_supply` - optional max supply of the token; if Some(), it will override max supply in contract JSON definition /// - /// Note that for conveniance, you can provide `steps` as a [`TestStep`] or a slice of tuples, where each tuple contains: + /// Note that for convenience, you can provide `steps` as a [`TestStep`] or a slice of tuples, where each tuple contains: /// * `height` - height at which claim will be made /// * `expected_balance` - expected balance after claim was made /// * `expect_pass` - whether we expect the claim to pass or not @@ -2738,7 +2612,7 @@ mod test_suite { identity: dpp::prelude::Identity, signer: SimpleSigner, identity_public_key: IdentityPublicKey, - token_id: Option, + token_id: Option, contract: Option, start_time: Option, token_distribution_type: TokenDistributionType, diff --git a/packages/rs-platform-version/src/version/v9.rs b/packages/rs-platform-version/src/version/v9.rs index abcc4fa5a54..3ff77c6d9b6 100644 --- a/packages/rs-platform-version/src/version/v9.rs +++ b/packages/rs-platform-version/src/version/v9.rs @@ -17,7 +17,6 @@ use crate::version::dpp_versions::DPPVersion; use crate::version::drive_abci_versions::drive_abci_method_versions::v6::DRIVE_ABCI_METHOD_VERSIONS_V6; use crate::version::drive_abci_versions::drive_abci_query_versions::v1::DRIVE_ABCI_QUERY_VERSIONS_V1; use crate::version::drive_abci_versions::drive_abci_structure_versions::v1::DRIVE_ABCI_STRUCTURE_VERSIONS_V1; -use crate::version::drive_abci_versions::drive_abci_validation_versions::v4::DRIVE_ABCI_VALIDATION_VERSIONS_V4; use crate::version::drive_abci_versions::drive_abci_validation_versions::v5::DRIVE_ABCI_VALIDATION_VERSIONS_V5; use crate::version::drive_abci_versions::drive_abci_withdrawal_constants::v2::DRIVE_ABCI_WITHDRAWAL_CONSTANTS_V2; use crate::version::drive_abci_versions::DriveAbciVersion;