use num_bigint::BigUint;
use num_traits::One;
#[cfg(test)]
use num_traits::Zero;
fn int_cuberoot_u256(x: &BigUint) -> BigUint {
let mut hi = BigUint::one();
while (&hi * &hi * &hi) < *x {
hi <<= 1;
}
let mut lo = &hi >> 1;
while lo < hi {
let mid = (&lo + &hi + BigUint::one()) >> 1;
let mid3 = &mid * &mid * ∣
if &mid3 <= x {
lo = mid;
} else {
hi = mid - BigUint::one();
}
}
lo
}
fn calculate_qscale_uint(block_time: u64) -> BigUint {
const Q: u32 = 42;
const GAMMA_FP: u64 = 3927365422841;
let t = BigUint::from(block_time);
let block_scaled = &t << (3 * Q);
let t_cbrt = int_cuberoot_u256(&block_scaled);
let numerator = &t << (2 * Q);
let denominator = (&t_cbrt * BigUint::from(GAMMA_FP)) >> Q;
(&numerator + (&denominator >> 1)) / &denominator
}
pub fn calculate_time_bended_deadline(raw_quality: u64, base_target: u64, block_time: u64) -> u64 {
const P: u32 = 21;
const Q: u32 = 42;
if raw_quality == 0 {
return 0;
}
let scale_q = calculate_qscale_uint(block_time);
let shift_3p = BigUint::one() << (3 * P);
let v = (BigUint::from(raw_quality) * &shift_3p) / BigUint::from(base_target);
let r = int_cuberoot_u256(&v);
let numer = &scale_q * &r;
let denom = BigUint::one() << (P + Q);
let rounded: BigUint = (&numer + (&denom >> 1)) / &denom;
let digits = rounded.to_u64_digits();
if digits.is_empty() {
0
} else {
digits[0]
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_cuberoot_basic() {
assert_eq!(int_cuberoot_u256(&BigUint::from(0u64)), BigUint::zero());
assert_eq!(int_cuberoot_u256(&BigUint::from(1u64)), BigUint::one());
assert_eq!(int_cuberoot_u256(&BigUint::from(8u64)), BigUint::from(2u64));
assert_eq!(
int_cuberoot_u256(&BigUint::from(27u64)),
BigUint::from(3u64)
);
}
#[test]
fn test_time_bending_zero() {
assert_eq!(calculate_time_bended_deadline(0, 100, 120), 0);
}
#[test]
fn test_time_bending_basic() {
let adjusted_quality = 1000u64;
let base_target = 265949979u64;
let raw_quality = adjusted_quality * base_target;
let poc_time = calculate_time_bended_deadline(raw_quality, base_target, 120);
println!(
"poc_time for adjusted_quality={}, base_target={}, block_time=120: {}",
adjusted_quality, base_target, poc_time
);
assert!(poc_time > 0, "poc_time should be > 0, got {}", poc_time);
assert!(
poc_time < 86400,
"poc_time should be < 86400, got {}",
poc_time
);
}
#[test]
fn test_time_bending_reference_values() {
let bt = 314467198u64;
let adjusted_quality = 103u64;
let raw_quality = adjusted_quality * bt;
println!(
"Testing: adjusted_quality={}, base_target={}",
adjusted_quality, bt
);
println!(
"raw_quality = {} * {} = {}",
adjusted_quality, bt, raw_quality
);
let result = calculate_time_bended_deadline(raw_quality, bt, 120);
println!(
"calculate_time_bended_deadline({}, {}, 120) = {}",
raw_quality, bt, result
);
println!("Expected: ~128 seconds");
assert!(
result > 100 && result < 150,
"Expected ~128, got {}",
result
);
}
}