Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
cd34fd0
test: initial version of framework and some first tests
lklimek Mar 17, 2025
2c338d6
WIP
lklimek Mar 18, 2025
938fea9
chore: test decreasing distribution
lklimek Mar 20, 2025
4a42235
chore: remove duplicate code
lklimek Mar 20, 2025
27bad8d
chore: self-review
lklimek Mar 20, 2025
53576a1
WIP
lklimek Mar 21, 2025
91de94c
chore: check entropy of random distribution
lklimek Mar 24, 2025
3dc1c9b
wip
lklimek Mar 25, 2025
a9cbf97
chore: wip
lklimek Mar 26, 2025
3adc24c
chore(Dockerfile): snapshots_enabled = true
lklimek Mar 27, 2025
b84b54b
Revert "chore(Dockerfile): snapshots_enabled = true"
lklimek Mar 27, 2025
1061b52
test: Polynomial
lklimek Apr 3, 2025
fe48a9e
test: Logarithmic
lklimek Apr 4, 2025
9045516
test: InvertedLogarithmic
lklimek Apr 4, 2025
3e9810d
fix: saturate instead of wrap on pseudorandom function
QuantumExplorer Apr 6, 2025
e25b027
test: validate distribution fn before use
lklimek Apr 7, 2025
99cd113
tmp: test_1_percent_increase_claim to prove issue
lklimek Apr 7, 2025
ac4cbc9
test: test max_token_redemption_cycles
lklimek Apr 7, 2025
962af28
test: refactor - don't use test_case crate
lklimek Apr 8, 2025
967be62
test: simplify mod names
lklimek Apr 8, 2025
c0ac2bf
test: further improvements
lklimek Apr 8, 2025
b55e618
test: minor fix
lklimek Apr 8, 2025
58e563c
Merge branch 'v2.0-dev' into test/token-dist-functions
QuantumExplorer Apr 14, 2025
3c04663
max distribution cycles
QuantumExplorer Apr 14, 2025
783d3a4
Merge branch 'v2.0-dev' into test/token-dist-functions
QuantumExplorer Apr 14, 2025
d1833a6
Merge branch 'test/token-dist-functions' into test/token-dist-functio…
QuantumExplorer Apr 14, 2025
f28a013
more fixes
QuantumExplorer Apr 15, 2025
018bef2
more work
QuantumExplorer Apr 15, 2025
efc37db
step decreasing distribution fixes
QuantumExplorer Apr 15, 2025
62fa908
more fixes
QuantumExplorer Apr 15, 2025
8c46d05
Merge branch 'v2.0-dev-distributions' into test/token-dist-functions-…
QuantumExplorer Apr 15, 2025
40a03c8
fmt
QuantumExplorer Apr 15, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,20 @@ impl Encode for DistributionFunction {
step_count,
decrease_per_interval_numerator,
decrease_per_interval_denominator,
s,
n,
start_decreasing_offset: s,
max_interval_count,
distribution_start_amount: n,
trailing_distribution_interval_amount,
min_value,
} => {
2u8.encode(encoder)?;
step_count.encode(encoder)?;
decrease_per_interval_numerator.encode(encoder)?;
decrease_per_interval_denominator.encode(encoder)?;
s.encode(encoder)?;
max_interval_count.encode(encoder)?;
n.encode(encoder)?;
trailing_distribution_interval_amount.encode(encoder)?;
min_value.encode(encoder)?;
}
DistributionFunction::Stepwise(steps) => {
Expand Down Expand Up @@ -60,7 +64,7 @@ impl Encode for DistributionFunction {
m,
n,
o,
start_moment: s,
start_moment,
b,
min_value,
max_value,
Expand All @@ -71,7 +75,7 @@ impl Encode for DistributionFunction {
m.encode(encoder)?;
n.encode(encoder)?;
o.encode(encoder)?;
s.encode(encoder)?;
start_moment.encode(encoder)?;
b.encode(encoder)?;
min_value.encode(encoder)?;
max_value.encode(encoder)?;
Expand Down Expand Up @@ -167,15 +171,19 @@ impl Decode for DistributionFunction {
let decrease_per_interval_numerator = u16::decode(decoder)?;
let decrease_per_interval_denominator = u16::decode(decoder)?;
let s = Option::<u64>::decode(decoder)?;
let max_interval_count = Option::<u16>::decode(decoder)?;
let n = TokenAmount::decode(decoder)?;
let trailing_distribution_interval_amount = TokenAmount::decode(decoder)?;
let min_value = Option::<u64>::decode(decoder)?;
Ok(Self::StepDecreasingAmount {
s,
start_decreasing_offset: s,
decrease_per_interval_numerator,
decrease_per_interval_denominator,
step_count,
n,
distribution_start_amount: n,
max_interval_count,
min_value,
trailing_distribution_interval_amount,
})
}
3 => {
Expand Down Expand Up @@ -313,14 +321,18 @@ impl<'de> BorrowDecode<'de> for DistributionFunction {
let decrease_per_interval_numerator = u16::borrow_decode(decoder)?;
let decrease_per_interval_denominator = u16::borrow_decode(decoder)?;
let s = Option::<u64>::borrow_decode(decoder)?;
let max_interval_count = Option::<u16>::borrow_decode(decoder)?;
let n = TokenAmount::borrow_decode(decoder)?;
let trailing_distribution_interval_amount = TokenAmount::borrow_decode(decoder)?;
let min_value = Option::<u64>::borrow_decode(decoder)?;
Ok(Self::StepDecreasingAmount {
step_count,
decrease_per_interval_numerator,
decrease_per_interval_denominator,
s,
n,
start_decreasing_offset: s,
max_interval_count,
distribution_start_amount: n,
trailing_distribution_interval_amount,
min_value,
})
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
use crate::balances::credits::TokenAmount;
use crate::data_contract::associated_token::token_perpetual_distribution::distribution_function::DistributionFunction;
use crate::data_contract::associated_token::token_perpetual_distribution::distribution_function::{
DistributionFunction, DEFAULT_STEP_DECREASING_AMOUNT_MAX_CYCLES_BEFORE_TRAILING_DISTRIBUTION,
};
use crate::ProtocolError;

impl DistributionFunction {
Expand Down Expand Up @@ -51,42 +53,52 @@ impl DistributionFunction {
step_count,
decrease_per_interval_numerator,
decrease_per_interval_denominator,
s,
n,
start_decreasing_offset,
max_interval_count,
distribution_start_amount,
trailing_distribution_interval_amount,
min_value,
} => {
// Check for division by zero in the denominator:
if *decrease_per_interval_denominator == 0 {
return Err(ProtocolError::DivideByZero(
"StepDecreasingAmount: denominator is 0",
));
}
let s_val = s.unwrap_or(contract_registration_step);
// Compute the number of steps passed.
let steps = if x > s_val {
(x - s_val) / (*step_count as u64)
} else {
0
};
let reduction = 1.0
- ((*decrease_per_interval_numerator as f64)
/ (*decrease_per_interval_denominator as f64));
let factor = reduction.powf(steps as f64);
let result = (*n as f64) * factor;
// Clamp to min_value if provided.
let clamped = if let Some(min) = min_value {
result.max(*min as f64)
} else {
result
};
if !clamped.is_finite() || clamped > (u64::MAX as f64) || clamped < 0.0 {
return Err(ProtocolError::Overflow(
"StepDecreasingAmount evaluation overflow or negative",
));

let s_val = start_decreasing_offset.unwrap_or(contract_registration_step);

if x <= s_val {
return Ok(*distribution_start_amount);
}
Ok(clamped as TokenAmount)
}

let steps_passed = (x - s_val) / (*step_count as u64);
let max_intervals = max_interval_count.unwrap_or(
DEFAULT_STEP_DECREASING_AMOUNT_MAX_CYCLES_BEFORE_TRAILING_DISTRIBUTION,
) as u64;

if steps_passed > max_intervals {
return Ok(*trailing_distribution_interval_amount);
}

let mut numerator = *distribution_start_amount;
let denominator = *decrease_per_interval_denominator as u64;
let reduction_numerator =
denominator.saturating_sub(*decrease_per_interval_numerator as u64);

for _ in 0..steps_passed {
numerator = numerator * reduction_numerator / denominator;
}

let mut result = numerator;

if let Some(min) = min_value {
if result < *min {
result = *min;
}
}

Ok(result)
}
DistributionFunction::Stepwise(steps) => {
// Return the emission corresponding to the greatest key <= x.
Ok(steps
Expand Down Expand Up @@ -501,8 +513,10 @@ mod tests {
step_count: 10,
decrease_per_interval_numerator: 1,
decrease_per_interval_denominator: 2, // 50% reduction per step
s: Some(0),
n: 100,
start_decreasing_offset: Some(0),
max_interval_count: None,
distribution_start_amount: 100,
trailing_distribution_interval_amount: 0,
min_value: Some(10),
};

Expand All @@ -520,8 +534,10 @@ mod tests {
step_count: 10,
decrease_per_interval_numerator: 1,
decrease_per_interval_denominator: 0, // Invalid denominator
s: Some(0),
n: 100,
start_decreasing_offset: Some(0),
max_interval_count: None,
distribution_start_amount: 100,
trailing_distribution_interval_amount: 0,
min_value: Some(10),
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,15 @@ mod validation;

pub const MAX_DISTRIBUTION_PARAM: u64 = 281_474_976_710_655; //u48::Max 2^48 - 1

/// The max cycles param is the upper limit of cycles the system can ever support
/// This is applied to linear distribution.
/// For all other distributions we use a versioned max cycles contained in the platform version.
/// That other version is much lower because the calculations for other distributions are more
/// complex.
pub const MAX_DISTRIBUTION_CYCLES_PARAM: u64 = 32_767; //u15::Max 2^(63 - 48) - 1

pub const DEFAULT_STEP_DECREASING_AMOUNT_MAX_CYCLES_BEFORE_TRAILING_DISTRIBUTION: u16 = 128;

pub const MAX_LINEAR_SLOPE_PARAM: u64 = 256;

#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, PartialOrd)]
Expand Down Expand Up @@ -90,8 +99,13 @@ pub enum DistributionFunction {
/// # Parameters
/// - `step_count`: The number of periods between each step.
/// - `decrease_per_interval_numerator` and `decrease_per_interval_denominator`: Define the reduction factor per step.
/// - `s`: Optional start period offset (e.g., start block or time). If not provided, the contract creation start is used.
/// - `n`: The initial token emission.
/// - `start_decreasing_offset`: Optional start period offset (e.g., start block or time). If not provided, the contract creation start is used.
/// If this is provided before this number we give out the distribution start amount every interval.
/// - `max_interval_count`: The maximum amount of intervals there can be. Can be up to 1024.
/// !!!Very important!!! -> This will default to 128 is default if not set.
/// This means that after 128 cycles we will be distributing trailing_distribution_interval_amount per interval.
/// - `distribution_start_amount`: The initial token emission.
/// - `trailing_distribution_interval_amount`: The token emission after all decreasing intervals.
/// - `min_value`: Optional minimum emission value.
///
/// # Use Case
Expand All @@ -105,8 +119,10 @@ pub enum DistributionFunction {
step_count: u32,
decrease_per_interval_numerator: u16,
decrease_per_interval_denominator: u16,
s: Option<u64>,
n: TokenAmount,
start_decreasing_offset: Option<u64>,
max_interval_count: Option<u16>,
distribution_start_amount: TokenAmount,
trailing_distribution_interval_amount: TokenAmount,
min_value: Option<u64>,
},

Expand Down Expand Up @@ -530,23 +546,35 @@ impl fmt::Display for DistributionFunction {
step_count,
decrease_per_interval_numerator,
decrease_per_interval_denominator,
s,
n,
start_decreasing_offset: s,
max_interval_count,
distribution_start_amount,
trailing_distribution_interval_amount,
min_value,
} => {
write!(
f,
"StepDecreasingAmount: {} tokens, decreasing by {}/{} every {} steps",
n,
distribution_start_amount,
decrease_per_interval_numerator,
decrease_per_interval_denominator,
step_count
)?;
if let Some(start) = s {
write!(f, " starting at period {}", start)?;
write!(f, ", starting at period {}", start)?;
}
if let Some(max_intervals) = max_interval_count {
write!(f, ", with a maximum of {} intervals", max_intervals)?;
} else {
write!(f, ", with a maximum of 128 intervals (default)")?;
}
write!(
f,
", trailing distribution amount {} tokens",
trailing_distribution_interval_amount
)?;
if let Some(min) = min_value {
write!(f, ", with a minimum emission of {}", min)?;
write!(f, ", minimum emission {} tokens", min)?;
}
Ok(())
}
Expand Down
Loading
Loading