implement muldiv without overflow and underflow

Signed-off-by: Uncle Stretch <uncle.stretch@ghostchain.io>
This commit is contained in:
Uncle Stretch
2025-08-10 16:41:14 +03:00
parent 9240f424e1
commit e2c75ca558
6 changed files with 323 additions and 26 deletions

View File

@@ -1295,8 +1295,10 @@ fn accumulated_commission_could_be_nullified() {
#[test]
fn bridged_inlation_reward_works() {
ExtBuilder::build().execute_with(|| {
let amount: u128 = 1337 * 1_000_000_000;
let commission: u128 = amount / 100; // 1% commission
let amount_full: u128 = 1337 * 1_000_000_000;
let commission: u128 = amount_full / 100; // 1% commission
let amount: u128 = amount_full - commission;
let total_staked_ideal: u128 = 69;
let total_staked_not_ideal: u128 = 68;
let total_issuance: u128 = 100;
@@ -1538,8 +1540,10 @@ fn bridged_inlation_reward_works() {
#[test]
fn bridged_inflation_era_payout_triggers_need_of_nullification() {
ExtBuilder::build().execute_with(|| {
let amount: u128 = 1337 * 1_000_000_000;
let commission: u128 = amount / 100; // 1% commission
let amount_full: u128 = 1337 * 1_000_000_000;
let commission: u128 = amount_full / 100; // 1% commission
let amount: u128 = amount_full - commission;
let total_staked_ideal: u128 = 69;
let total_issuance: u128 = 100;
@@ -1570,3 +1574,165 @@ fn trigger_nullification_works_as_expected() {
assert_eq!(NullifyNeeded::<Test>::get(), false);
});
}
#[test]
fn check_substrate_guarantees_not_to_overflow() {
ExtBuilder::build().execute_with(|| {
let reward_curve = RewardCurve::get();
let mut n: u128 = 69;
let mut d: u128 = 100;
loop {
n = match n.checked_mul(1_000_000) {
Some(value) => value,
None => break,
};
d = match d.checked_mul(1_000_000) {
Some(value) => value,
None => break,
};
assert_eq!(
reward_curve.calculate_for_fraction_times_denominator(n, d),
d
);
}
});
}
#[test]
fn check_muldiv_guarantees_not_to_overflow() {
ExtBuilder::build().execute_with(|| {
let mut a: u128 = 2;
let mut b: u128 = 3;
let mut c: u128 = 6;
let mut result: u128 = 1;
loop {
a = match a.checked_mul(1_000_000) {
Some(value) => value,
None => break,
};
b = match b.checked_mul(1_000_000) {
Some(value) => value,
None => break,
};
c = match c.checked_mul(1_000_000) {
Some(value) => value,
None => break,
};
result = match result.checked_mul(1_000_000) {
Some(value) => value,
None => break,
};
assert_eq!(MulDiv::<u128>::calculate(a, b, c), result);
}
assert_eq!(
MulDiv::<u128>::calculate(u128::MAX, u128::MAX, u128::MAX),
u128::MAX
);
assert_eq!(MulDiv::<u128>::calculate(u128::MAX, 0, 0), 0);
assert_eq!(MulDiv::<u128>::calculate(0, u128::MAX, 0), 0);
assert_eq!(MulDiv::<u128>::calculate(0, 0, u128::MAX), 0);
});
}
#[test]
fn check_bridged_inflation_curve_for_overflow() {
ExtBuilder::build().execute_with(|| {
let amount_full: u128 = 1337 * 1_000_000_000;
let commission: u128 = amount_full / 100; // 1% commission
let amount: u128 = amount_full - commission;
let tollerance: u128 = commission / 100; // 1% tollerance
let precomputed_payout: u128 = 13177568884;
let precomputed_rest: u128 = 192431116;
assert_eq!(precomputed_payout + precomputed_rest, commission);
let mut total_staked_ideal: u128 = 69_000;
let mut total_staked_not_ideal: u128 = 68_000;
let mut total_issuance: u128 = 100_000;
assert_ok!(GhostNetworks::accumulate_commission(&commission));
assert_ok!(GhostNetworks::accumulate_incoming_imbalance(&amount));
loop {
total_staked_ideal = match total_staked_ideal.checked_mul(1_000_000) {
Some(value) => value,
None => break,
};
total_staked_not_ideal = match total_staked_not_ideal.checked_mul(1_000_000) {
Some(value) => value,
None => break,
};
total_issuance = match total_issuance.checked_mul(1_000_000) {
Some(value) => value,
None => break,
};
assert_eq!(
BridgedInflationCurve::<RewardCurve, Test>::era_payout(
total_staked_ideal,
total_issuance + amount,
0
),
(commission, 0)
);
let (payout, rest) = BridgedInflationCurve::<RewardCurve, Test>::era_payout(
total_staked_not_ideal,
total_issuance + amount,
0,
);
let payout_deviation = if precomputed_payout > payout {
precomputed_payout - payout
} else {
payout - precomputed_payout
};
let rest_deviation = if precomputed_rest > rest {
precomputed_rest - rest
} else {
rest - precomputed_rest
};
assert!(payout_deviation < tollerance);
assert!(rest_deviation < tollerance);
}
});
}
#[test]
fn check_bridged_inflation_curve_for_big_commissions() {
ExtBuilder::build().execute_with(|| {
let mut amount_full: u128 = 1337;
let total_staked_ideal: u128 = 69_000_000;
let total_issuance: u128 = 100_000_000;
loop {
amount_full = match amount_full.checked_mul(1_000_000) {
Some(value) => value,
None => break,
};
let commission: u128 = amount_full / 100; // 1% commission
let amount: u128 = amount_full - commission;
AccumulatedCommission::<Test>::set(commission);
BridgedImbalance::<Test>::set(BridgeAdjustment {
bridged_in: amount,
bridged_out: 0,
});
assert_eq!(
BridgedInflationCurve::<RewardCurve, Test>::era_payout(
total_staked_ideal,
total_issuance + amount,
0
),
(commission, 0)
);
}
});
}