bsv/primitives/
polynomial.rs1use crate::primitives::big_number::{BigNumber, Endian};
8use crate::primitives::curve::Curve;
9use crate::primitives::error::PrimitivesError;
10use crate::primitives::utils::{base58_decode, base58_encode};
11
12#[derive(Clone, Debug)]
17pub struct PointInFiniteField {
18 pub x: BigNumber,
19 pub y: BigNumber,
20}
21
22impl PointInFiniteField {
23 pub fn new(x: BigNumber, y: BigNumber) -> Self {
25 let curve = Curve::secp256k1();
26 let x_mod = x.umod(&curve.p).unwrap_or(x);
27 let y_mod = y.umod(&curve.p).unwrap_or(y);
28 PointInFiniteField { x: x_mod, y: y_mod }
29 }
30
31 pub fn to_string_repr(&self) -> String {
33 let x_bytes = self.x.to_array(Endian::Big, None);
34 let y_bytes = self.y.to_array(Endian::Big, None);
35 format!("{}.{}", base58_encode(&x_bytes), base58_encode(&y_bytes))
36 }
37
38 pub fn from_string_repr(s: &str) -> Result<Self, PrimitivesError> {
40 let parts: Vec<&str> = s.split('.').collect();
41 if parts.len() != 2 {
42 return Err(PrimitivesError::InvalidFormat(format!(
43 "Expected 'x.y' format, got: {}",
44 s
45 )));
46 }
47 let x_bytes = base58_decode(parts[0])?;
48 let y_bytes = base58_decode(parts[1])?;
49 let x = BigNumber::from_bytes(&x_bytes, Endian::Big);
50 let y = BigNumber::from_bytes(&y_bytes, Endian::Big);
51 Ok(PointInFiniteField::new(x, y))
52 }
53}
54
55pub struct Polynomial {
61 pub points: Vec<PointInFiniteField>,
62 pub threshold: usize,
63}
64
65impl Polynomial {
66 pub fn new(points: Vec<PointInFiniteField>, threshold: Option<usize>) -> Self {
70 let t = threshold.unwrap_or(points.len());
71 Polynomial {
72 points,
73 threshold: t,
74 }
75 }
76
77 pub fn from_private_key(key_bytes: &[u8], threshold: usize) -> Self {
82 let curve = Curve::secp256k1();
83 let key_bn = BigNumber::from_bytes(key_bytes, Endian::Big);
84
85 let mut points = vec![PointInFiniteField::new(BigNumber::zero(), key_bn)];
87
88 for _ in 1..threshold {
90 let random_x_bytes = crate::primitives::random::random_bytes(32);
91 let random_y_bytes = crate::primitives::random::random_bytes(32);
92 let random_x = BigNumber::from_bytes(&random_x_bytes, Endian::Big)
93 .umod(&curve.p)
94 .unwrap_or(BigNumber::one());
95 let random_y = BigNumber::from_bytes(&random_y_bytes, Endian::Big)
96 .umod(&curve.p)
97 .unwrap_or(BigNumber::one());
98 points.push(PointInFiniteField::new(random_x, random_y));
99 }
100
101 Polynomial::new(points, Some(threshold))
102 }
103
104 pub fn value_at(&self, x: &BigNumber) -> BigNumber {
110 let curve = Curve::secp256k1();
111 let p = &curve.p;
112
113 let mut y = BigNumber::zero();
114
115 for i in 0..self.threshold {
116 let mut term = self.points[i].y.clone();
117
118 for j in 0..self.threshold {
119 if i != j {
120 let xj = &self.points[j].x;
121 let xi = &self.points[i].x;
122
123 let numerator = x.sub(xj).umod(p).unwrap_or(BigNumber::zero());
125
126 let denominator = xi.sub(xj).umod(p).unwrap_or(BigNumber::zero());
128
129 let denominator_inverse = denominator.invm(p).unwrap_or(BigNumber::zero());
131
132 let fraction = numerator
134 .mul(&denominator_inverse)
135 .umod(p)
136 .unwrap_or(BigNumber::zero());
137
138 term = term.mul(&fraction).umod(p).unwrap_or(BigNumber::zero());
140 }
141 }
142
143 y = y.add(&term).umod(p).unwrap_or(BigNumber::zero());
144 }
145
146 y
147 }
148}
149
150#[cfg(test)]
155mod tests {
156 use super::*;
157
158 #[test]
159 fn test_point_in_finite_field_new() {
160 let p = PointInFiniteField::new(BigNumber::from_number(5), BigNumber::from_number(10));
161 assert_eq!(p.x.cmp(&BigNumber::from_number(5)), 0);
162 assert_eq!(p.y.cmp(&BigNumber::from_number(10)), 0);
163 }
164
165 #[test]
166 fn test_point_in_finite_field_mod_p() {
167 let curve = Curve::secp256k1();
168 let large = curve.p.add(&BigNumber::from_number(1));
170 let p = PointInFiniteField::new(large, BigNumber::from_number(5));
171 assert_eq!(p.x.cmp(&BigNumber::from_number(1)), 0);
172 }
173
174 #[test]
175 fn test_point_in_finite_field_string_roundtrip() {
176 let p =
177 PointInFiniteField::new(BigNumber::from_number(12345), BigNumber::from_number(67890));
178 let s = p.to_string_repr();
179 let recovered = PointInFiniteField::from_string_repr(&s).unwrap();
180 assert_eq!(recovered.x.cmp(&p.x), 0);
181 assert_eq!(recovered.y.cmp(&p.y), 0);
182 }
183
184 #[test]
185 fn test_polynomial_value_at_intercept() {
186 let points = vec![
188 PointInFiniteField::new(BigNumber::zero(), BigNumber::from_number(42)),
189 PointInFiniteField::new(BigNumber::one(), BigNumber::from_number(100)),
190 ];
191 let poly = Polynomial::new(points, Some(2));
192 let val = poly.value_at(&BigNumber::zero());
193 assert_eq!(val.cmp(&BigNumber::from_number(42)), 0);
194 }
195
196 #[test]
197 fn test_polynomial_value_at_known_point() {
198 let points = vec![
200 PointInFiniteField::new(BigNumber::zero(), BigNumber::from_number(42)),
201 PointInFiniteField::new(BigNumber::one(), BigNumber::from_number(100)),
202 ];
203 let poly = Polynomial::new(points, Some(2));
204 let val = poly.value_at(&BigNumber::one());
205 assert_eq!(val.cmp(&BigNumber::from_number(100)), 0);
206 }
207
208 #[test]
209 fn test_polynomial_lagrange_three_points() {
210 let points = vec![
213 PointInFiniteField::new(BigNumber::from_number(1), BigNumber::from_number(5)),
214 PointInFiniteField::new(BigNumber::from_number(2), BigNumber::from_number(10)),
215 PointInFiniteField::new(BigNumber::from_number(3), BigNumber::from_number(17)),
216 ];
217 let poly = Polynomial::new(points, Some(3));
218 let secret = poly.value_at(&BigNumber::zero());
219
220 assert_eq!(secret.cmp(&BigNumber::from_number(2)), 0);
227 }
228
229 #[test]
230 fn test_polynomial_from_private_key() {
231 let key_bytes = BigNumber::from_number(42).to_array(Endian::Big, Some(32));
232 let poly = Polynomial::from_private_key(&key_bytes, 3);
233 assert_eq!(poly.points.len(), 3);
234 assert_eq!(poly.threshold, 3);
235
236 assert_eq!(poly.points[0].x.cmp(&BigNumber::zero()), 0);
238 assert_eq!(poly.points[0].y.cmp(&BigNumber::from_number(42)), 0);
239 }
240
241 #[test]
242 fn test_polynomial_from_private_key_reconstruct() {
243 let secret = BigNumber::from_number(123456789);
246 let key_bytes = secret.to_array(Endian::Big, Some(32));
247 let poly = Polynomial::from_private_key(&key_bytes, 3);
248
249 let shares: Vec<PointInFiniteField> = (1..=3)
251 .map(|i| {
252 let x = BigNumber::from_number(i);
253 let y = poly.value_at(&x);
254 PointInFiniteField::new(x, y)
255 })
256 .collect();
257
258 let recon_poly = Polynomial::new(shares, Some(3));
260 let recovered = recon_poly.value_at(&BigNumber::zero());
261 assert_eq!(recovered.cmp(&secret), 0, "Should recover original secret");
262 }
263}