use crate::error::{Error, Result};
use crate::primitives::encoding::{from_base58, to_base58};
use crate::primitives::BigNumber;
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct PointInFiniteField {
pub x: BigNumber,
pub y: BigNumber,
}
impl PointInFiniteField {
pub fn new(x: BigNumber, y: BigNumber) -> Self {
let p = BigNumber::secp256k1_prime();
Self {
x: x.modulo(&p),
y: y.modulo(&p),
}
}
pub fn from_string(s: &str) -> Result<Self> {
let parts: Vec<&str> = s.split('.').collect();
if parts.len() != 2 {
return Err(Error::InvalidBase58(format!(
"Invalid point string format: expected 'base58(x).base58(y)', got '{}'",
s
)));
}
let x_bytes = from_base58(parts[0])?;
let y_bytes = from_base58(parts[1])?;
let x = BigNumber::from_bytes_be(&x_bytes);
let y = BigNumber::from_bytes_be(&y_bytes);
Ok(Self::new(x, y))
}
pub fn to_point_string(&self) -> String {
let x_bytes = self.x.to_bytes_be_min();
let y_bytes = self.y.to_bytes_be_min();
let x_str = if x_bytes.is_empty() {
"1".to_string() } else {
to_base58(&x_bytes)
};
let y_str = if y_bytes.is_empty() {
"1".to_string()
} else {
to_base58(&y_bytes)
};
format!("{}.{}", x_str, y_str)
}
}
impl std::fmt::Display for PointInFiniteField {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.to_point_string())
}
}
#[derive(Clone, Debug)]
pub struct Polynomial {
pub points: Vec<PointInFiniteField>,
pub threshold: usize,
}
impl Polynomial {
pub fn new(points: Vec<PointInFiniteField>, threshold: usize) -> Self {
Self { points, threshold }
}
pub fn value_at(&self, x: &BigNumber) -> BigNumber {
let p = BigNumber::secp256k1_prime();
let mut y = BigNumber::zero();
let num_points = std::cmp::min(self.points.len(), self.threshold);
for i in 0..num_points {
let mut term = self.points[i].y.clone();
for j in 0..num_points {
if i != j {
let numerator = x.sub(&self.points[j].x).modulo(&p);
let denominator = self.points[i].x.sub(&self.points[j].x).modulo(&p);
let denominator_inv = denominator.mod_inverse(&p).unwrap_or_else(|| {
BigNumber::zero()
});
let fraction = numerator.mul(&denominator_inv).modulo(&p);
term = term.mul(&fraction).modulo(&p);
}
}
y = y.add(&term).modulo(&p);
}
y
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_point_in_finite_field_new() {
let p = BigNumber::secp256k1_prime();
let point = PointInFiniteField::new(BigNumber::from_u64(1), BigNumber::from_u64(2));
assert_eq!(point.x, BigNumber::from_u64(1));
assert_eq!(point.y, BigNumber::from_u64(2));
let large_x = p.add(&BigNumber::from_u64(5));
let point = PointInFiniteField::new(large_x, BigNumber::from_u64(2));
assert_eq!(point.x, BigNumber::from_u64(5));
}
#[test]
fn test_point_in_finite_field_string_roundtrip() {
let point = PointInFiniteField::new(BigNumber::from_u64(12345), BigNumber::from_u64(67890));
let s = point.to_point_string();
let parsed = PointInFiniteField::from_string(&s).unwrap();
assert_eq!(point, parsed);
}
#[test]
fn test_point_in_finite_field_from_string() {
let original =
PointInFiniteField::new(BigNumber::from_u64(12345), BigNumber::from_u64(67890));
let s = original.to_point_string();
let parsed = PointInFiniteField::from_string(&s).unwrap();
assert_eq!(parsed.x, BigNumber::from_u64(12345));
assert_eq!(parsed.y, BigNumber::from_u64(67890));
}
#[test]
fn test_point_in_finite_field_invalid_format() {
assert!(PointInFiniteField::from_string("abc").is_err());
assert!(PointInFiniteField::from_string("a.b.c").is_err());
}
#[test]
fn test_polynomial_linear() {
let p5 = BigNumber::from_u64(5);
let p8 = BigNumber::from_u64(8);
let points = vec![
PointInFiniteField::new(BigNumber::from_u64(1), p8.clone()),
PointInFiniteField::new(BigNumber::from_u64(2), BigNumber::from_u64(11)),
];
let poly = Polynomial::new(points, 2);
let y0 = poly.value_at(&BigNumber::zero());
assert_eq!(y0, p5);
let y1 = poly.value_at(&BigNumber::from_u64(1));
assert_eq!(y1, p8);
}
#[test]
fn test_polynomial_quadratic() {
let points = vec![
PointInFiniteField::new(BigNumber::from_u64(1), BigNumber::from_u64(6)),
PointInFiniteField::new(BigNumber::from_u64(2), BigNumber::from_u64(11)),
PointInFiniteField::new(BigNumber::from_u64(3), BigNumber::from_u64(18)),
];
let poly = Polynomial::new(points, 3);
let y0 = poly.value_at(&BigNumber::zero());
assert_eq!(y0, BigNumber::from_u64(3));
}
#[test]
fn test_polynomial_with_large_numbers() {
let p = BigNumber::secp256k1_prime();
let y1 = p.sub(&BigNumber::from_u64(10)); let y2 = p.sub(&BigNumber::from_u64(5));
let points = vec![
PointInFiniteField::new(BigNumber::from_u64(1), y1),
PointInFiniteField::new(BigNumber::from_u64(2), y2),
];
let poly = Polynomial::new(points, 2);
let y0 = poly.value_at(&BigNumber::zero());
let expected = p.sub(&BigNumber::from_u64(15));
assert_eq!(y0, expected);
}
#[test]
fn test_polynomial_constant() {
let secret = BigNumber::from_hex("deadbeef").unwrap();
let points = vec![PointInFiniteField::new(
BigNumber::from_u64(1),
secret.clone(),
)];
let poly = Polynomial::new(points, 1);
let y0 = poly.value_at(&BigNumber::zero());
assert_eq!(y0, secret);
}
}