use crate::primitives::big_number::{BigNumber, Endian};
use crate::primitives::curve::Curve;
use crate::primitives::error::PrimitivesError;
use crate::primitives::utils::{base58_decode, base58_encode};
#[derive(Clone, Debug)]
pub struct PointInFiniteField {
pub x: BigNumber,
pub y: BigNumber,
}
impl PointInFiniteField {
pub fn new(x: BigNumber, y: BigNumber) -> Self {
let curve = Curve::secp256k1();
let x_mod = x.umod(&curve.p).unwrap_or(x);
let y_mod = y.umod(&curve.p).unwrap_or(y);
PointInFiniteField { x: x_mod, y: y_mod }
}
pub fn to_string_repr(&self) -> String {
let x_bytes = self.x.to_array(Endian::Big, None);
let y_bytes = self.y.to_array(Endian::Big, None);
format!("{}.{}", base58_encode(&x_bytes), base58_encode(&y_bytes))
}
pub fn from_string_repr(s: &str) -> Result<Self, PrimitivesError> {
let parts: Vec<&str> = s.split('.').collect();
if parts.len() != 2 {
return Err(PrimitivesError::InvalidFormat(format!(
"Expected 'x.y' format, got: {}",
s
)));
}
let x_bytes = base58_decode(parts[0])?;
let y_bytes = base58_decode(parts[1])?;
let x = BigNumber::from_bytes(&x_bytes, Endian::Big);
let y = BigNumber::from_bytes(&y_bytes, Endian::Big);
Ok(PointInFiniteField::new(x, y))
}
}
pub struct Polynomial {
pub points: Vec<PointInFiniteField>,
pub threshold: usize,
}
impl Polynomial {
pub fn new(points: Vec<PointInFiniteField>, threshold: Option<usize>) -> Self {
let t = threshold.unwrap_or(points.len());
Polynomial {
points,
threshold: t,
}
}
pub fn from_private_key(key_bytes: &[u8], threshold: usize) -> Self {
let curve = Curve::secp256k1();
let key_bn = BigNumber::from_bytes(key_bytes, Endian::Big);
let mut points = vec![PointInFiniteField::new(BigNumber::zero(), key_bn)];
for _ in 1..threshold {
let random_x_bytes = crate::primitives::random::random_bytes(32);
let random_y_bytes = crate::primitives::random::random_bytes(32);
let random_x = BigNumber::from_bytes(&random_x_bytes, Endian::Big)
.umod(&curve.p)
.unwrap_or(BigNumber::one());
let random_y = BigNumber::from_bytes(&random_y_bytes, Endian::Big)
.umod(&curve.p)
.unwrap_or(BigNumber::one());
points.push(PointInFiniteField::new(random_x, random_y));
}
Polynomial::new(points, Some(threshold))
}
pub fn value_at(&self, x: &BigNumber) -> BigNumber {
let curve = Curve::secp256k1();
let p = &curve.p;
let mut y = BigNumber::zero();
for i in 0..self.threshold {
let mut term = self.points[i].y.clone();
for j in 0..self.threshold {
if i != j {
let xj = &self.points[j].x;
let xi = &self.points[i].x;
let numerator = x.sub(xj).umod(p).unwrap_or(BigNumber::zero());
let denominator = xi.sub(xj).umod(p).unwrap_or(BigNumber::zero());
let denominator_inverse = denominator.invm(p).unwrap_or(BigNumber::zero());
let fraction = numerator
.mul(&denominator_inverse)
.umod(p)
.unwrap_or(BigNumber::zero());
term = term.mul(&fraction).umod(p).unwrap_or(BigNumber::zero());
}
}
y = y.add(&term).umod(p).unwrap_or(BigNumber::zero());
}
y
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_point_in_finite_field_new() {
let p = PointInFiniteField::new(BigNumber::from_number(5), BigNumber::from_number(10));
assert_eq!(p.x.cmp(&BigNumber::from_number(5)), 0);
assert_eq!(p.y.cmp(&BigNumber::from_number(10)), 0);
}
#[test]
fn test_point_in_finite_field_mod_p() {
let curve = Curve::secp256k1();
let large = curve.p.add(&BigNumber::from_number(1));
let p = PointInFiniteField::new(large, BigNumber::from_number(5));
assert_eq!(p.x.cmp(&BigNumber::from_number(1)), 0);
}
#[test]
fn test_point_in_finite_field_string_roundtrip() {
let p =
PointInFiniteField::new(BigNumber::from_number(12345), BigNumber::from_number(67890));
let s = p.to_string_repr();
let recovered = PointInFiniteField::from_string_repr(&s).unwrap();
assert_eq!(recovered.x.cmp(&p.x), 0);
assert_eq!(recovered.y.cmp(&p.y), 0);
}
#[test]
fn test_polynomial_value_at_intercept() {
let points = vec![
PointInFiniteField::new(BigNumber::zero(), BigNumber::from_number(42)),
PointInFiniteField::new(BigNumber::one(), BigNumber::from_number(100)),
];
let poly = Polynomial::new(points, Some(2));
let val = poly.value_at(&BigNumber::zero());
assert_eq!(val.cmp(&BigNumber::from_number(42)), 0);
}
#[test]
fn test_polynomial_value_at_known_point() {
let points = vec![
PointInFiniteField::new(BigNumber::zero(), BigNumber::from_number(42)),
PointInFiniteField::new(BigNumber::one(), BigNumber::from_number(100)),
];
let poly = Polynomial::new(points, Some(2));
let val = poly.value_at(&BigNumber::one());
assert_eq!(val.cmp(&BigNumber::from_number(100)), 0);
}
#[test]
fn test_polynomial_lagrange_three_points() {
let points = vec![
PointInFiniteField::new(BigNumber::from_number(1), BigNumber::from_number(5)),
PointInFiniteField::new(BigNumber::from_number(2), BigNumber::from_number(10)),
PointInFiniteField::new(BigNumber::from_number(3), BigNumber::from_number(17)),
];
let poly = Polynomial::new(points, Some(3));
let secret = poly.value_at(&BigNumber::zero());
assert_eq!(secret.cmp(&BigNumber::from_number(2)), 0);
}
#[test]
fn test_polynomial_from_private_key() {
let key_bytes = BigNumber::from_number(42).to_array(Endian::Big, Some(32));
let poly = Polynomial::from_private_key(&key_bytes, 3);
assert_eq!(poly.points.len(), 3);
assert_eq!(poly.threshold, 3);
assert_eq!(poly.points[0].x.cmp(&BigNumber::zero()), 0);
assert_eq!(poly.points[0].y.cmp(&BigNumber::from_number(42)), 0);
}
#[test]
fn test_polynomial_from_private_key_reconstruct() {
let secret = BigNumber::from_number(123456789);
let key_bytes = secret.to_array(Endian::Big, Some(32));
let poly = Polynomial::from_private_key(&key_bytes, 3);
let shares: Vec<PointInFiniteField> = (1..=3)
.map(|i| {
let x = BigNumber::from_number(i);
let y = poly.value_at(&x);
PointInFiniteField::new(x, y)
})
.collect();
let recon_poly = Polynomial::new(shares, Some(3));
let recovered = recon_poly.value_at(&BigNumber::zero());
assert_eq!(recovered.cmp(&secret), 0, "Should recover original secret");
}
}