use crate::convert::{affine_to_gpu, scalar_be_to_limbs};
use crate::crypto::{Point, U256};
use crate::gpu::{GpuAffinePoint, GpuKangaroo};
use crate::math::negate_256_be;
use anyhow::Result;
use k256::{ProjectivePoint, Scalar};
use k256::elliptic_curve::ops::{MulByGenerator, Reduce};
use k256::U256 as K256U256;
use rayon::prelude::*;
pub fn generate_jump_table(range_bits: u32) -> (Vec<GpuAffinePoint>, Vec<[u32; 8]>) {
const TABLE_SIZE: usize = 256;
let mut points = Vec::with_capacity(TABLE_SIZE);
let mut distances = Vec::with_capacity(TABLE_SIZE);
let mean_exp = range_bits / 2;
for i in 0..TABLE_SIZE {
let mut h = 0x811c9dc5u32;
h = (h ^ (i as u32)).wrapping_mul(0x01000193);
let num_bytes = (mean_exp + 7) / 8;
let limit_byte = (num_bytes as usize).min(32);
let mut scalar_bytes = [0u8; 32];
for b in (32 - limit_byte)..32 {
h = (h ^ (b as u32)).wrapping_mul(0x01000193);
scalar_bytes[b] = (h & 0xFF) as u8;
}
let rem = mean_exp % 8;
if rem != 0 {
let mask = (1u8 << rem) - 1;
if 32 - limit_byte < 32 {
scalar_bytes[32 - limit_byte] &= mask;
}
} else if limit_byte > 0 {
}
if scalar_bytes.iter().all(|&x| x == 0) {
scalar_bytes[31] = 1;
}
let scalar_uint = K256U256::from_be_slice(&scalar_bytes);
let scalar = Scalar::reduce(scalar_uint);
let point = ProjectivePoint::mul_by_generator(&scalar);
let affine = point.to_affine();
points.push(affine_to_gpu(&affine));
distances.push(scalar_be_to_limbs(&scalar_bytes));
}
for i in 0..4u32 {
tracing::debug!("Jump table[{}] generated", i);
}
(points, distances)
}
pub fn initialize_kangaroos(
pubkey: &Point,
start: &U256,
range_bits: u32,
num_kangaroos: u32,
) -> Result<Vec<GpuKangaroo>> {
let half = num_kangaroos / 2;
let range_size = if range_bits >= 128 { u128::MAX } else { 1u128 << range_bits };
let range_middle = if range_bits >= 128 { u128::MAX / 2 } else { 1u128 << (range_bits - 1) };
tracing::debug!(
"Kangaroo init: range_bits={}, range_size=0x{:x}, range_middle=0x{:x}",
range_bits,
range_size,
range_middle
);
let grid_delta = if num_kangaroos > 0 {
range_size / (num_kangaroos as u128)
} else {
range_size
};
let kangaroos: Vec<GpuKangaroo> = (0..num_kangaroos)
.into_par_iter()
.map(|i| {
let is_tame = i < half;
let grid_pos = (i as u128) * grid_delta;
let prng_seed = hash_seed(i, 0xCAFEBABE);
let jitter = prng_seed % (grid_delta / 2 + 1);
let offset = (grid_pos + jitter) % range_size;
let (point, dist) = if is_tame {
init_tame_kangaroo_at_offset(start, offset)
} else {
init_wild_kangaroo_at_offset(pubkey, offset, range_middle)
};
let gpu_point = affine_to_gpu(&point);
GpuKangaroo {
x: gpu_point.x,
y: gpu_point.y,
z: [1, 0, 0, 0, 0, 0, 0, 0], dist,
ktype: if is_tame { 0 } else { 1 },
is_active: 1,
_padding: [0; 2],
}
})
.collect();
Ok(kangaroos)
}
fn hash_seed(index: u32, salt: u64) -> u128 {
let mut h = 0xcbf29ce484222325u64;
h ^= index as u64;
h = h.wrapping_mul(0x100000001b3); h ^= salt;
h = h.wrapping_mul(0x100000001b3);
h ^= h >> 33;
h = h.wrapping_mul(0xff51afd7ed558ccd);
h ^= h >> 33;
let h2 = h.wrapping_mul(0xc4ceb9fe1a85ec53) ^ (index as u64).wrapping_mul(0x9e3779b97f4a7c15);
((h as u128) << 64) | (h2 as u128)
}
fn init_tame_kangaroo_at_offset(start: &U256, offset: u128) -> (k256::AffinePoint, [u32; 8]) {
let start_uint = K256U256::from_le_slice(start);
let mut offset_le = [0u8; 32];
offset_le[0..16].copy_from_slice(&offset.to_le_bytes());
let offset_uint = K256U256::from_le_slice(&offset_le);
let sum = start_uint.wrapping_add(&offset_uint);
let scalar = Scalar::reduce(sum);
let point = ProjectivePoint::mul_by_generator(&scalar);
let mut offset_bytes = [0u8; 32];
offset_bytes[16..].copy_from_slice(&offset.to_be_bytes());
(point.to_affine(), scalar_be_to_limbs(&offset_bytes))
}
fn init_wild_kangaroo_at_offset(
pubkey: &Point,
raw_offset: u128,
range_middle: u128,
) -> (k256::AffinePoint, [u32; 8]) {
let centered_offset = raw_offset as i128 - range_middle as i128;
if centered_offset >= 0 {
let offset = centered_offset as u128;
let mut offset_bytes = [0u8; 32];
offset_bytes[16..].copy_from_slice(&offset.to_be_bytes());
let scalar_uint = K256U256::from_be_slice(&offset_bytes);
let scalar = Scalar::reduce(scalar_uint);
let offset_point = ProjectivePoint::mul_by_generator(&scalar);
let wild_point = *pubkey + offset_point;
(wild_point.to_affine(), scalar_be_to_limbs(&offset_bytes))
} else {
let abs_offset = (-centered_offset) as u128;
let mut offset_bytes = [0u8; 32];
offset_bytes[16..].copy_from_slice(&abs_offset.to_be_bytes());
let scalar_uint = K256U256::from_be_slice(&offset_bytes);
let scalar = Scalar::reduce(scalar_uint);
let offset_point = ProjectivePoint::mul_by_generator(&scalar);
let wild_point = *pubkey - offset_point;
let neg_offset_bytes = negate_256_be(&offset_bytes);
(wild_point.to_affine(), scalar_be_to_limbs(&neg_offset_bytes))
}
}