arnaucube
arnaucube/testudoPublic Self-hosted repository
testudo/src/unipoly.rs
src/unipoly.rsRaw
  1use crate::poseidon_transcript::{AppendToPoseidon, PoseidonTranscript};
  2
  3use super::commitments::{Commitments, MultiCommitGens};
  4use super::group::GroupElement;
  5use super::scalar::Scalar;
  6use super::transcript::{AppendToTranscript, ProofTranscript};
  7use ark_ff::Field;
  8use ark_serialize::*;
  9use merlin::Transcript;
 10// ax^2 + bx + c stored as vec![c,b,a]
 11// ax^3 + bx^2 + cx + d stored as vec![d,c,b,a]
 12#[derive(Debug, CanonicalDeserialize, CanonicalSerialize, Clone)]
 13pub struct UniPoly {
 14  pub coeffs: Vec<Scalar>,
 15  // pub coeffs_fq: Vec<Fq>,
 16}
 17
 18// ax^2 + bx + c stored as vec![c,a]
 19// ax^3 + bx^2 + cx + d stored as vec![d,b,a]
 20#[derive(CanonicalSerialize, CanonicalDeserialize, Debug)]
 21pub struct CompressedUniPoly {
 22  pub coeffs_except_linear_term: Vec<Scalar>,
 23}
 24
 25impl UniPoly {
 26  pub fn from_evals(evals: &[Scalar]) -> Self {
 27    // we only support degree-2 or degree-3 univariate polynomials
 28    assert!(evals.len() == 3 || evals.len() == 4);
 29    let coeffs = if evals.len() == 3 {
 30      // ax^2 + bx + c
 31      let two_inv = Scalar::from(2).inverse().unwrap();
 32
 33      let c = evals[0];
 34      let a = two_inv * (evals[2] - evals[1] - evals[1] + c);
 35      let b = evals[1] - c - a;
 36      vec![c, b, a]
 37    } else {
 38      // ax^3 + bx^2 + cx + d
 39      let two_inv = Scalar::from(2).inverse().unwrap();
 40      let six_inv = Scalar::from(6).inverse().unwrap();
 41
 42      let d = evals[0];
 43      let a = six_inv
 44        * (evals[3] - evals[2] - evals[2] - evals[2] + evals[1] + evals[1] + evals[1] - evals[0]);
 45      let b = two_inv
 46        * (evals[0] + evals[0] - evals[1] - evals[1] - evals[1] - evals[1] - evals[1]
 47          + evals[2]
 48          + evals[2]
 49          + evals[2]
 50          + evals[2]
 51          - evals[3]);
 52      let c = evals[1] - d - a - b;
 53      vec![d, c, b, a]
 54    };
 55
 56    UniPoly { coeffs }
 57  }
 58
 59  pub fn degree(&self) -> usize {
 60    self.coeffs.len() - 1
 61  }
 62
 63  pub fn as_vec(&self) -> Vec<Scalar> {
 64    self.coeffs.clone()
 65  }
 66
 67  pub fn eval_at_zero(&self) -> Scalar {
 68    self.coeffs[0]
 69  }
 70
 71  pub fn eval_at_one(&self) -> Scalar {
 72    (0..self.coeffs.len()).map(|i| self.coeffs[i]).sum()
 73  }
 74
 75  pub fn evaluate(&self, r: &Scalar) -> Scalar {
 76    let mut eval = self.coeffs[0];
 77    let mut power = *r;
 78    for i in 1..self.coeffs.len() {
 79      eval += power * self.coeffs[i];
 80      power *= r;
 81    }
 82    eval
 83  }
 84
 85  pub fn compress(&self) -> CompressedUniPoly {
 86    let coeffs_except_linear_term = [&self.coeffs[..1], &self.coeffs[2..]].concat();
 87    assert_eq!(coeffs_except_linear_term.len() + 1, self.coeffs.len());
 88    CompressedUniPoly {
 89      coeffs_except_linear_term,
 90    }
 91  }
 92
 93  pub fn commit(&self, gens: &MultiCommitGens, blind: &Scalar) -> GroupElement {
 94    self.coeffs.commit(blind, gens)
 95  }
 96}
 97
 98impl CompressedUniPoly {
 99  // we require eval(0) + eval(1) = hint, so we can solve for the linear term as:
100  // linear_term = hint - 2 * constant_term - deg2 term - deg3 term
101  pub fn decompress(&self, hint: &Scalar) -> UniPoly {
102    let mut linear_term =
103      (*hint) - self.coeffs_except_linear_term[0] - self.coeffs_except_linear_term[0];
104    for i in 1..self.coeffs_except_linear_term.len() {
105      linear_term -= self.coeffs_except_linear_term[i];
106    }
107
108    let mut coeffs = vec![self.coeffs_except_linear_term[0], linear_term];
109    coeffs.extend(&self.coeffs_except_linear_term[1..]);
110    assert_eq!(self.coeffs_except_linear_term.len() + 1, coeffs.len());
111    UniPoly { coeffs }
112  }
113}
114
115impl AppendToPoseidon for UniPoly {
116  fn append_to_poseidon(&self, transcript: &mut PoseidonTranscript) {
117    // transcript.append_message(label, b"UniPoly_begin");
118    for i in 0..self.coeffs.len() {
119      transcript.append_scalar(&self.coeffs[i]);
120    }
121    // transcript.append_message(label, b"UniPoly_end");
122  }
123}
124
125impl AppendToTranscript for UniPoly {
126  fn append_to_transcript(&self, label: &'static [u8], transcript: &mut Transcript) {
127    transcript.append_message(label, b"UniPoly_begin");
128    for i in 0..self.coeffs.len() {
129      transcript.append_scalar(b"coeff", &self.coeffs[i]);
130    }
131    transcript.append_message(label, b"UniPoly_end");
132  }
133}
134
135#[cfg(test)]
136mod tests {
137
138  use ark_ff::One;
139
140  use super::*;
141
142  #[test]
143  fn test_from_evals_quad() {
144    // polynomial is 2x^2 + 3x + 1
145    let e0 = Scalar::one();
146    let e1 = Scalar::from(6);
147    let e2 = Scalar::from(15);
148    let evals = vec![e0, e1, e2];
149    let poly = UniPoly::from_evals(&evals);
150
151    assert_eq!(poly.eval_at_zero(), e0);
152    assert_eq!(poly.eval_at_one(), e1);
153    assert_eq!(poly.coeffs.len(), 3);
154    assert_eq!(poly.coeffs[0], Scalar::one());
155    assert_eq!(poly.coeffs[1], Scalar::from(3));
156    assert_eq!(poly.coeffs[2], Scalar::from(2));
157
158    let hint = e0 + e1;
159    let compressed_poly = poly.compress();
160    let decompressed_poly = compressed_poly.decompress(&hint);
161    for i in 0..decompressed_poly.coeffs.len() {
162      assert_eq!(decompressed_poly.coeffs[i], poly.coeffs[i]);
163    }
164
165    let e3 = Scalar::from(28);
166    assert_eq!(poly.evaluate(&Scalar::from(3)), e3);
167  }
168
169  #[test]
170  fn test_from_evals_cubic() {
171    // polynomial is x^3 + 2x^2 + 3x + 1
172    let e0 = Scalar::one();
173    let e1 = Scalar::from(7);
174    let e2 = Scalar::from(23);
175    let e3 = Scalar::from(55);
176    let evals = vec![e0, e1, e2, e3];
177    let poly = UniPoly::from_evals(&evals);
178
179    assert_eq!(poly.eval_at_zero(), e0);
180    assert_eq!(poly.eval_at_one(), e1);
181    assert_eq!(poly.coeffs.len(), 4);
182    assert_eq!(poly.coeffs[0], Scalar::one());
183    assert_eq!(poly.coeffs[1], Scalar::from(3));
184    assert_eq!(poly.coeffs[2], Scalar::from(2));
185    assert_eq!(poly.coeffs[3], Scalar::from(1));
186
187    let hint = e0 + e1;
188    let compressed_poly = poly.compress();
189    let decompressed_poly = compressed_poly.decompress(&hint);
190    for i in 0..decompressed_poly.coeffs.len() {
191      assert_eq!(decompressed_poly.coeffs[i], poly.coeffs[i]);
192    }
193
194    let e4 = Scalar::from(109);
195    assert_eq!(poly.evaluate(&Scalar::from(4)), e4);
196  }
197}