From 21c2e2f3cd7367c5486bf8282737c9cec6f3a85e Mon Sep 17 00:00:00 2001 From: adria0 Date: Fri, 17 Sep 2021 18:03:40 +0200 Subject: [PATCH] cleanup --- .gitignore | 10 + Cargo.toml | 8 + LICENSE | 21 ++ README.md | 6 + src/constrains.rs | 167 +++++++++++++ src/ec.rs | 312 ++++++++++++++++++++++++ src/field.rs | 243 +++++++++++++++++++ src/lib.rs | 7 + src/matrix.rs | 215 +++++++++++++++++ src/plonk.rs | 21 ++ src/poly.rs | 475 ++++++++++++++++++++++++++++++++++++ src/prover.rs | 596 ++++++++++++++++++++++++++++++++++++++++++++++ 12 files changed, 2081 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.toml create mode 100644 LICENSE create mode 100644 README.md create mode 100644 src/constrains.rs create mode 100644 src/ec.rs create mode 100644 src/field.rs create mode 100644 src/lib.rs create mode 100644 src/matrix.rs create mode 100644 src/plonk.rs create mode 100644 src/poly.rs create mode 100644 src/prover.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..088ba6b --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +# Generated by Cargo +# will have compiled files and executables +/target/ + +# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries +# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html +Cargo.lock + +# These are backup files generated by rustfmt +**/*.rs.bk diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..caa3b31 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "plonk-by-hand" +version = "0.1.0" +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..2246acd --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 adria0.eth + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..54e6e94 --- /dev/null +++ b/README.md @@ -0,0 +1,6 @@ +# plonk-by-fingers + +This is a quick & dirty implementationm of the excellent Joshua Fitzgerald [Plonk by hand](https://research.metastate.dev/plonk-by-hand-part-1) ([part2](https://research.metastate.dev/plonk-by-hand-part-2-the-proof)) ([part3](https://research.metastate.dev/plonk-by-hand-part-3-verification)) tutorial + +- do not expect this code to be anything close to production, is intended just to understand the protocol +- there is a mistake in the hand computations in part3 that says that where the correct value seems to be , this also affects the pairing, that is instead diff --git a/src/constrains.rs b/src/constrains.rs new file mode 100644 index 0000000..173d935 --- /dev/null +++ b/src/constrains.rs @@ -0,0 +1,167 @@ +#![allow(dead_code, unused_imports)] + +use std::fmt::Display; + +use super::ec::{g1f, g2f, G1Point, G2Point}; +use super::field::Field; +use super::matrix::Matrix; +use super::plonk::{f101, F17, P17}; +use super::poly::Poly; + +// (q_l * a) + (q_r * b) + (q_o * c) + (q_m * a * b) + q_c = 0 +// where a,b,c are the left, right and output wires of the gate +pub struct Gate { + pub q_l: F17, + pub q_r: F17, + pub q_o: F17, + pub q_m: F17, + pub q_c: F17, +} + +impl Gate { + pub fn new(q_l: F17, q_r: F17, q_o: F17, q_m: F17, q_c: F17) -> Self { + Gate { + q_l, + q_r, + q_o, + q_m, + q_c, + } + } + pub fn sum_a_b() -> Self { + Gate { + q_l: F17::one(), + q_r: F17::one(), + q_o: -F17::one(), + q_m: F17::zero(), + q_c: F17::zero(), + } + } + pub fn mul_a_b() -> Self { + Gate { + q_l: F17::zero(), + q_r: F17::zero(), + q_o: -F17::one(), + q_m: F17::one(), + q_c: F17::zero(), + } + } + pub fn bind_a(value: F17) -> Self { + Gate { + q_l: F17::one(), + q_r: F17::zero(), + q_o: F17::zero(), + q_m: F17::one(), + q_c: value, + } + } +} + +#[derive(Debug)] +pub enum CopyOf { + A(usize), + B(usize), + C(usize), +} + +impl Display for Gate { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!( + f, + "{}a+{}b+{}ab+{}c+{}=0", + self.q_l, self.q_r, self.q_o, self.q_m, self.q_c + ) + } +} + +#[derive(Debug)] +pub struct Constrains { + pub q_l: Vec, + pub q_r: Vec, + pub q_o: Vec, + pub q_m: Vec, + pub q_c: Vec, + pub c_a: Vec, + pub c_b: Vec, + pub c_c: Vec, +} + +pub struct Assigment { + pub a: F17, + pub b: F17, + pub c: F17, +} + +impl Assigment { + pub fn new(a: F17, b: F17, c: F17) -> Self { + Self { a, b, c } + } +} + +pub struct Assigments { + pub a: Vec, + pub b: Vec, + pub c: Vec, +} + +impl Constrains { + pub fn new(gates: &[Gate], copy_constrains: (Vec, Vec, Vec)) -> Self { + Self { + q_l: gates.iter().map(|g| g.q_l).collect(), + q_r: gates.iter().map(|g| g.q_r).collect(), + q_o: gates.iter().map(|g| g.q_o).collect(), + q_m: gates.iter().map(|g| g.q_m).collect(), + q_c: gates.iter().map(|g| g.q_c).collect(), + c_a: copy_constrains.0, + c_b: copy_constrains.1, + c_c: copy_constrains.2, + } + } + + pub fn satisfies(&self, v: &Assigments) -> bool { + // check gates (q_l * a) + (q_r * b) + (q_o * c) + (q_m * a * b) + q_c = 0 + assert_eq!(v.a.len(), self.q_l.len()); + for n in 0..v.a.len() { + let r = self.q_l[n] * v.a[n] + + self.q_l[n] * v.b[n] + + self.q_o[n] * v.c[n] + + self.q_m[n] * v.a[n] * v.b[n] + + self.q_c[n]; + if r != Field::zero() { + return false; + } + } + + // check copy constrains + assert_eq!(v.a.len(), self.c_a.len()); + assert_eq!(v.a.len(), self.c_b.len()); + assert_eq!(v.a.len(), self.c_c.len()); + for n in 0..self.c_a.len() { + let value = |c: &CopyOf| match c { + CopyOf::A(n) => &v.a[*n - 1], + CopyOf::B(n) => &v.b[*n - 1], + CopyOf::C(n) => &v.c[*n - 1], + }; + if &v.a[n] != value(&self.c_a[n]) + || &v.b[n] != value(&self.c_b[n]) + || &v.c[n] != value(&self.c_c[n]) + { + return false; + } + } + true + } +} + +impl Assigments { + pub fn new(assigments: &[Assigment]) -> Self { + Self { + a: assigments.iter().map(|v| v.a).collect(), + b: assigments.iter().map(|v| v.b).collect(), + c: assigments.iter().map(|v| v.c).collect(), + } + } + pub fn len(&self) -> usize { + self.a.len() + } +} diff --git a/src/ec.rs b/src/ec.rs new file mode 100644 index 0000000..9566a53 --- /dev/null +++ b/src/ec.rs @@ -0,0 +1,312 @@ +#![allow(dead_code)] + +use std::{ + fmt::Display, + ops::{Add, Mul, Neg}, +}; + +use super::plonk::{f101, F101}; + +#[allow(non_snake_case)] +pub fn g1f(x: u64, y: u64) -> G1Point { + G1Point::new(f101(x), f101(y)) +} + +#[allow(non_snake_case)] +pub fn g2f(a: u64, b: u64) -> G2Point { + G2Point::new(f101(a), f101(b)) +} + +#[derive(Debug, Copy, Clone, PartialEq)] +pub struct G1Point { + pub x: F101, + pub y: F101, + pub inf: bool, +} +impl G1Point { + pub fn new(x: F101, y: F101) -> Self { + G1Point { x, y, inf: false } + } + pub fn in_curve(&self) -> bool { + self.y.pow(2) == self.x.pow(3) + f101(3) + } + pub fn is_inf(&self) -> bool { + self.inf + } + pub fn inf() -> Self { + G1Point { + x: f101(0), + y: f101(0), + inf: true, + } + } +} + +#[derive(Debug, Copy, Clone, PartialEq)] +pub struct G2Point { + pub a: F101, + pub b: F101, +} +impl G2Point { + // a + b ยท u + pub fn new(a: F101, b: F101) -> Self { + G2Point { a, b } + } + + pub fn pow(&self, mut n: u64) -> Self { + // frobenious map reduction: p^101 = -p + let (mut p, mut base) = if n >= 101 { + let base = -self.pow(n / 101); + n = n % 101; + (base, *self) + } else { + (G2Point::new(f101(1), f101(0)), *self) + }; + + // montgomery reduction + while n > 0 { + if n % 2 == 1 { + p = p * base; + } + n = n >> 1; + base = base * base; + } + + p + } +} + +impl Display for G1Point { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + if self.inf { + write!(f, "inf") + } else { + write!(f, "({},{})", self.x, self.y) + } + } +} + +impl Display for G2Point { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "{}+{}u", self.a, self.b) + } +} + +impl Neg for G1Point { + type Output = G1Point; + fn neg(self) -> Self::Output { + if self.inf { + self + } else { + G1Point::new(self.x, -self.y) + } + } +} + +impl Neg for G2Point { + type Output = G2Point; + fn neg(self) -> Self::Output { + G2Point::new(self.a, -self.b) + } +} + +impl Add for G1Point { + type Output = G1Point; + fn add(self, rhs: G1Point) -> Self { + if self.inf { + rhs + } else if rhs.inf { + self + } else if self == -rhs { + G1Point::inf() + } else if self == rhs { + let two = F101::from(2); + let three = F101::from(3); + let m = ((three * self.x.pow(2)) / (two * self.y)).unwrap(); + G1Point::new( + m * m - two * self.x, + m * (three * self.x - m.pow(2)) - self.y, + ) + } else { + // https://en.wikipedia.org/wiki/Elliptic_curve_point_multiplication#G1Point_addition + let lambda = ((rhs.y - self.y) / (rhs.x - self.x)) + .expect(&format!("cannot add {}+{}", self, rhs)); + let x = lambda.pow(2) - self.x - rhs.x; + G1Point::new(x, lambda * (self.x - x) - self.y) + } + } +} + +impl Add for G2Point { + type Output = G2Point; + fn add(self, rhs: G2Point) -> Self { + if self == rhs { + let two = F101::from(2); + let three = F101::from(3); + let m_u = ((three * self.a.pow(2)) / (two * self.b)).unwrap(); // in u units + let u_pow_2_inv = (-F101::from(2)).inv().unwrap(); // 1/(u^2) = -1/2 + let m_pow_2 = m_u.pow(2) * u_pow_2_inv; + G2Point::new( + m_pow_2 - two * self.a, + u_pow_2_inv * m_u * (three * self.a - m_pow_2) - self.b, + ) + } else { + let lambda_u = ((rhs.b - self.b) / (rhs.a - self.a)).unwrap(); + let lambda_pow_2 = lambda_u.pow(2) * -f101(2); + let a = lambda_pow_2 - self.a - rhs.a; + let b = lambda_u * (self.a - a) - self.b; + + G2Point::new(a, b) + } + } +} + +impl Mul for G1Point { + type Output = G1Point; + fn mul(self, rhs: F101) -> Self::Output { + let mut rhs = rhs.as_u64(); + if rhs == 0 || self.is_inf() { + return G1Point::inf(); + } + let mut result = None; + let mut base = self; + while rhs > 0 { + if rhs % 2 == 1 { + result = Some(if let Some(result) = result { + result + base + } else { + base + }) + } + rhs = rhs >> 1; + base = base + base; + } + result.unwrap() + } +} + +impl Mul for G2Point { + type Output = G2Point; + fn mul(self, rhs: F101) -> Self::Output { + let mut rhs = rhs.as_u64(); + let mut result = None; + let mut base = self; + while rhs > 0 { + if rhs % 2 == 1 { + result = Some(if let Some(result) = result { + result + base + } else { + base + }) + } + rhs = rhs >> 1; + base = base + base; + } + result.unwrap() + } +} + +impl Mul for G2Point { + type Output = G2Point; + fn mul(self, rhs: G2Point) -> Self::Output { + G2Point::new( + self.a * rhs.a - f101(2) * self.b * rhs.b, + self.a * rhs.b + self.b * rhs.a, + ) + } +} + +fn pairing_f(r: u64, p: G1Point, q: G2Point) -> G2Point { + // line equation from a to b point + let l = |a: G1Point, b: G1Point| { + let m = b.x - a.x; + let n = b.y - a.y; + + let x = n; + let y = -m; + let c = m * a.y - n * a.x; + + (x, y, c) + }; + + if r == 1 { + g2f(1, 0) + } else if r % 2 == 1 { + let r = r - 1; + let (x, y, c) = l(p * f101(r), p); + pairing_f(r, p, q) * G2Point::new(q.a * x + c, q.b * y) + } else { + let r = r / 2; + let (x, y, c) = l(p * f101(r), -p * f101(r) * f101(2)); + pairing_f(r, p, q).pow(2) * G2Point::new(q.a * x + c, q.b * y) + } +} + +pub fn pairing(g1: G1Point, g2: G2Point) -> G2Point { + let p = 101u64; + let r = 17u64; + let k = 2u32; + + let exp = (p.pow(k) - 1) / r; + + pairing_f(17, g1, g2).pow(exp) +} + +#[cfg(test)] +mod tests { + use super::*; + #[test] + fn test_g1_vectors() { + let g = g1f(1, 2); + let two_g = g + g; + let four_g = two_g + two_g; + let eight_g = four_g + four_g; + let sixteen_g = eight_g + eight_g; + + assert_eq!(g1f(1, 99), -g); + assert_eq!(g1f(68, 74), two_g); + assert_eq!(g1f(68, 27), -two_g); + assert_eq!(g1f(65, 98), four_g); + assert_eq!(g1f(65, 3), -four_g); + assert_eq!(g1f(18, 49), eight_g); + assert_eq!(g1f(18, 52), -eight_g); + assert_eq!(g1f(1, 99), sixteen_g); + assert_eq!(g1f(1, 2), -sixteen_g); + + // since g = -16 g, this subgroup has order 17 + + assert_eq!(g1f(26, 45), two_g + g); + assert_eq!(g1f(12, 32), four_g + g); + assert_eq!(g1f(18, 52), eight_g + g); + assert_eq!(four_g + two_g, two_g + four_g); + + assert_eq!(g * f101(1), g); + assert_eq!(g * f101(2), g + g); + assert_eq!(g * f101(6), g + g + g + g + g + g); + } + #[test] + fn test_g2_vectors() { + let g = g2f(36, 31); + + // check point doubling + assert_eq!(g2f(90, 82), g + g); + + // check point addition + assert_eq!((g + g) + (g + g), g + g + g + g); + + // check point multiplication + assert_eq!(g * f101(6), g + g + g + g + g + g); + + // // check frobenious map + // assert_eq!(-g, g * 100); + + // check G2 multiplication + assert_eq!(g2f(26, 97) * g2f(93, 76), g2f(97, 89)); + + // check G2 exp + assert_eq!(g2f(42, 49).pow(6), g2f(97, 89)); + assert_eq!(g2f(93, 76).pow(101), -g2f(93, 76)); + assert_eq!(g2f(93, 76).pow(102), (-g2f(93, 76)) * g2f(93, 76)); + assert_eq!(g2f(68, 47).pow(600), g2f(97, 89)); + } +} diff --git a/src/field.rs b/src/field.rs new file mode 100644 index 0000000..cf08fdc --- /dev/null +++ b/src/field.rs @@ -0,0 +1,243 @@ +#![allow(dead_code)] + +use super::poly::Poly; +use std::{ + fmt::Display, + ops::{Add, AddAssign, Div, Mul, MulAssign, Neg, Sub, SubAssign}, +}; + +fn extended_gcd(a: i64, b: i64) -> (i64, i64, i64) { + let (mut s, mut old_s) = (0, 1); + let (mut t, mut old_t) = (1, 0); + let (mut r, mut old_r) = (b, a); + + while r != 0 { + let quotient = &old_r / &r; + old_r -= "ient * &r; + std::mem::swap(&mut old_r, &mut r); + old_s -= "ient * &s; + std::mem::swap(&mut old_s, &mut s); + old_t -= quotient * &t; + std::mem::swap(&mut old_t, &mut t); + } + (old_r, old_s, old_t) +} + +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct Field(u64); + +#[allow(non_snake_case)] +pub fn Fi64(n: i64) -> Field { + if n < 0 { + -Field::from(-n as u64) + } else { + Field::from(n as u64) + } +} + +impl Field { + pub fn from(n: u64) -> Self { + Self(n % M) + } + pub fn zero() -> Self { + Self::from(0) + } + pub fn is_zero(&self) -> bool { + self.0 == 0 + } + pub fn one() -> Self { + Self::from(1) + } + pub fn as_u64(&self) -> u64 { + self.0 + } + pub fn in_field(&self) -> bool { + self.0 < M + } + pub fn rebase(&self) -> Field { + Field::::from(self.0) + } + pub fn inv(&self) -> Option { + let (gcd, c, _) = extended_gcd(self.0 as i64, M as i64); + if gcd == 1 { + if c < 0 { + Some(Self((M as i64 + c) as u64)) + } else { + Some(Self(c as u64)) + } + } else { + None + } + } + pub fn pow(&self, mut exp: u64) -> Self { + let mut result = Self::one(); + let mut base = *self; + while exp > 0 { + if exp % 2 == 1 { + result = result * base; + } + exp = exp >> 1; + base = base * base; + } + result + } + pub fn as_poly(&self) -> Poly { + Poly::new(vec![*self]) + } +} + +impl Display for Field { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "{}", self.0) + } +} + +impl Add for Field { + type Output = Field; + fn add(self, rhs: Self) -> Self::Output { + Field((self.0 + rhs.0) % M) + } +} + +impl Add<&Field> for Field { + type Output = Field; + fn add(self, rhs: &Field) -> Self::Output { + Field((self.0 + rhs.0) % M) + } +} + +impl Add> for &Field { + type Output = Field; + fn add(self, rhs: Field) -> Self::Output { + Field((self.0 + rhs.0) % M) + } +} + +impl Add<&Field> for &Field { + type Output = Field; + fn add(self, rhs: &Field) -> Self::Output { + Field((self.0 + rhs.0) % M) + } +} + +impl AddAssign<&Field> for Field { + fn add_assign(&mut self, rhs: &Field) { + self.0 = (self.0 + rhs.0) % M; + } +} + +impl AddAssign> for Field { + fn add_assign(&mut self, rhs: Field) { + self.0 = (self.0 + rhs.0) % M; + } +} + +impl Sub for Field { + type Output = Field; + fn sub(self, rhs: Self) -> Self::Output { + self + -rhs + } +} + +impl SubAssign<&Field> for Field { + fn sub_assign(&mut self, rhs: &Field) { + *self += -rhs; + } +} + +impl Neg for Field { + type Output = Field; + fn neg(self) -> Self::Output { + Field((M - self.0) % M) + } +} + +impl Neg for &Field { + type Output = Field; + fn neg(self) -> Self::Output { + Field((M - self.0) % M) + } +} + +impl Mul for Field { + type Output = Field; + fn mul(self, rhs: Self) -> Self::Output { + Field((self.0 * rhs.0) % M) + } +} + +impl Mul<&Field> for &Field { + type Output = Field; + fn mul(self, rhs: &Field) -> Self::Output { + Field((self.0 * rhs.0) % M) + } +} + +impl Mul<&Field> for Field { + type Output = Field; + fn mul(self, rhs: &Field) -> Self::Output { + Field((self.0 * rhs.0) % M) + } +} + +impl Mul> for &Field { + type Output = Field; + fn mul(self, rhs: Field) -> Self::Output { + Field((self.0 * rhs.0) % M) + } +} + +impl Mul> for &Field { + type Output = Poly; + fn mul(self, rhs: Poly) -> Self::Output { + rhs * self + } +} + +impl Mul> for Field { + type Output = Poly; + fn mul(self, rhs: Poly) -> Self::Output { + rhs * self + } +} + +impl MulAssign<&Field> for Field { + fn mul_assign(&mut self, rhs: &Field) { + self.0 = (self.0 * rhs.0) % M; + } +} + +impl Div for Field { + type Output = Option>; + + fn div(self, rhs: Self) -> Self::Output { + rhs.inv().map(|v| v * self) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + fn f101(n: u64) -> Field<101> { + Field::from(n) + } + + #[test] + fn test_base() { + assert_eq!(f101(200), f101(100) + f101(100)); + assert_eq!(f101(100), f101(0) - f101(1)); + assert_eq!(f101(100), f101(0) - f101(1)); + assert_eq!(None, f101(1) / f101(0)); + assert_eq!(f101(4), f101(12) * (f101(4) / f101(12)).unwrap()); + } + #[test] + fn test_vectors() { + assert_eq!(f101(100), -f101(1)); + assert_eq!(f101(50), -(f101(1) / f101(2)).unwrap()); + assert_eq!(f101(20), -(f101(1) / f101(5)).unwrap()); + assert_eq!(f101(1), f101(100).pow(0)); + assert_eq!(f101(100) * f101(100), f101(100).pow(2)); + assert_eq!(f101(100) * f101(100) * f101(100), f101(100).pow(3)); + } +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..97458e5 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,7 @@ +pub mod constrains; +pub mod ec; +pub mod field; +pub mod matrix; +pub mod plonk; +pub mod poly; +pub mod prover; diff --git a/src/matrix.rs b/src/matrix.rs new file mode 100644 index 0000000..e3a1541 --- /dev/null +++ b/src/matrix.rs @@ -0,0 +1,215 @@ +#!allow[(unused_imports, dead_code)] + +use std::{ + fmt::Display, + ops::{Add, Index, IndexMut, Mul}, +}; + +use super::field::Field; +use super::poly::Poly; + +#[derive(Debug, PartialEq, Clone)] +pub struct Matrix { + m: usize, // rows + n: usize, // cols + v: Vec>, +} + +impl Matrix { + pub fn zero(m: usize, n: usize) -> Self { + let mut v = Vec::new(); + v.resize(n * m, Field::::from(0)); + Self { m, n, v } + } + pub fn new(v: &[Field], m: usize, n: usize) -> Self { + assert_eq!(v.len(), m * n); + Matrix { + m, + n, + v: v.to_owned(), + } + } + + pub fn from(v: &[u64], m: usize, n: usize) -> Self { + assert_eq!(v.len(), m * n); + Matrix { + m, + n, + v: v.iter().map(|x| Field::::from(*x)).collect(), + } + } + pub fn cols(&self) -> usize { + self.n + } + pub fn rows(&self) -> usize { + self.m + } + pub fn inv(&self) -> Matrix { + let len = self.n; + let mut aug = Matrix::zero(len, len * 2); + for i in 0..len { + for j in 0..len { + aug[(i, j)] = self[(i, j)]; + } + aug[(i, i + len)] = Field::one(); + } + + aug.gauss_jordan_general(); + + let mut unaug = Matrix::zero(len, len); + for i in 0..len { + for j in 0..len { + unaug[(i, j)] = aug[(i, j + len)]; + } + } + unaug + } + + //Begin Generalised Reduced Row Echelon Form + fn gauss_jordan_general(&mut self) { + let mut lead = 0; + let row_count = self.m; + let col_count = self.n; + + for r in 0..row_count { + if col_count <= lead { + break; + } + let mut i = r; + while self[(i, lead)] == Field::zero() { + i = i + 1; + if row_count == i { + i = r; + lead = lead + 1; + if col_count == lead { + break; + } + } + } + + // swap i,r rows + for col in 0..self.n { + self.v.swap(self.n * i + col, self.n * r + col); + } + + if self[(r, lead)] != Field::zero() { + let div = self[(r, lead)]; + for j in 0..col_count { + self[(r, j)] = (self[(r, j)] / div).unwrap(); + } + } + + for k in 0..row_count { + if k != r { + let mult = self[(k, lead)]; + for j in 0..col_count { + self[(k, j)] = self[(k, j)] - self[(r, j)] * mult; + } + } + } + lead = lead + 1; + } + } + pub fn into_poly(self) -> Poly { + assert_eq!(1, self.n); + Poly::new(self.v) + } +} + +impl Index<(usize, usize)> for Matrix { + type Output = Field; + // row, column + fn index(&self, p: (usize, usize)) -> &Self::Output { + assert!(p.0 < self.m && p.1 < self.n); + &self.v[p.1 + p.0 * self.n] + } +} + +impl IndexMut<(usize, usize)> for Matrix { + fn index_mut(&mut self, p: (usize, usize)) -> &mut Self::Output { + assert!(p.0 < self.m && p.1 < self.n); + &mut self.v[p.1 + p.0 * self.n] + } +} + +impl Mul> for Matrix { + type Output = Matrix; + fn mul(self, rhs: Matrix) -> Self::Output { + &self * rhs + } +} + +impl Mul> for &Matrix { + type Output = Matrix; + fn mul(self, rhs: Matrix) -> Self::Output { + assert!(self.n == rhs.m); + let mut c = Matrix::zero(self.m, rhs.n); + for i in 0..self.m { + for j in 0..rhs.n { + for k in 0..self.n { + c[(i, j)] = c[(i, j)] + self[(i, k)] * rhs[(k, j)]; + } + } + } + c + } +} + +impl Add> for Matrix { + type Output = Matrix; + fn add(self, rhs: Matrix) -> Self::Output { + assert!(self.m == rhs.m); + assert!(self.n == rhs.n); + let mut c = Matrix::zero(self.m, self.n); + for i in 0..self.v.len() { + c.v[i] = self.v[i] + rhs.v[i]; + } + c + } +} + +impl Display for Matrix { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + writeln!(f, "{}x{}", self.m, self.n)?; + for r in 0..self.m { + write!(f, "[")?; + for c in 0..self.n - 1 { + write!(f, "{} ", self[(r, c)])?; + } + writeln!(f, "{}]", self[(r, self.n - 1)])?; + } + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + type F = Field<104729>; + type M = Matrix<104729>; + #[test] + fn test_sum_matrix() { + assert_eq!( + M::from(&[1, 2], 2, 1) + M::from(&[3, 4], 2, 1), + M::from(&[4, 6], 2, 1) + ); + } + #[test] + fn test_index_matrix() { + let m = M::from(&[1, 2, 3, 4], 2, 2); + // row 0 column 1 + assert_eq!(m[(0, 1)], F::from(2)); + } + #[test] + fn test_mul_matrix() { + assert_eq!( + M::from(&[1, 2, 3, 4, 5, 6], 2, 3) * M::from(&[10, 11, 20, 21, 30, 31], 3, 2), + M::from(&[140, 146, 320, 335], 2, 2) + ); + } + #[test] + fn test_inv_matrix() { + let m = M::from(&[1, 2, 3, 4, 1, 6, 7, 8, 9], 3, 3); + assert_eq!(m, m.inv().inv()); + } +} diff --git a/src/plonk.rs b/src/plonk.rs new file mode 100644 index 0000000..5fc7eb9 --- /dev/null +++ b/src/plonk.rs @@ -0,0 +1,21 @@ +#![allow(dead_code, unused_imports)] + +use std::fmt::Display; + +use super::field::Field; +use super::poly::Poly; + +pub type F17 = Field<17>; +pub type P17 = Poly<17>; +pub type F101 = Field<101>; + +pub fn f101(n: u64) -> F101 { + F101::from(n) +} + +pub fn f17(n: u64) -> F17 { + F17::from(n) +} +pub fn p17(n: &[i64]) -> P17 { + P17::from(n) +} diff --git a/src/poly.rs b/src/poly.rs new file mode 100644 index 0000000..baf3412 --- /dev/null +++ b/src/poly.rs @@ -0,0 +1,475 @@ +//! This module provides an implementation of polinomials over bls12_381::Field + +pub use super::field::Field; +use std::{ + cmp::max, + fmt::{Display, Formatter, Result}, + iter, + ops::{Add, AddAssign, Div, Mul, MulAssign, Sub, SubAssign}, +}; + +/// A polinomial with bl12_381::Field coeffs +#[derive(Clone, Debug, PartialEq)] +pub struct Poly(Vec>); + +impl Poly { + /// Creates a new Poly from its `coeffs`icients, first element the coefficient for x^0 + /// for safetly, input value is normalized (trailing zeroes are removed) + pub fn new(coeffs: Vec>) -> Self { + let mut poly = Poly(coeffs); + poly.normalize(); + poly + } + + /// Creates a new polinomial where the `coeffs` fits in u64 values + pub fn from(coeffs: &[i64]) -> Self { + Poly::new( + coeffs + .iter() + .map(|n| { + if *n >= 0 { + Field::::from(*n as u64) + } else { + -Field::::from(-*n as u64) + } + }) + .collect::>>(), + ) + } + pub fn coeffs(&self) -> &[Field] { + &self.0 + } + /// Returns p(x)=0 + pub fn zero() -> Self { + Poly(vec![Field::::zero()]) + } + + /// Returns p(x)=1 + pub fn one() -> Self { + Poly(vec![Field::::one()]) + } + + /// Creates a polinomial that contains a set of `p` points, by using lagrange + /// see https://en.wikipedia.org/wiki/Lagrange_polynomial + pub fn lagrange(p: &[(Field, Field)]) -> Self { + let k = p.len(); + let mut l = Poly::zero(); + for j in 0..k { + let mut l_j = Poly::one(); + for i in 0..k { + if i != j { + let c = (p[j].0 - p[i].0).inv(); + assert!( + bool::from(c.is_some()), + "lagrange polinomial x points must be unique" + ); + let c = c.unwrap(); + l_j = &l_j * &Poly::new(vec![-(c * p[i].0), c]); + } + } + l += &(&l_j * &p[j].1); + } + l + } + /// Creates a polinomial that has roots at the selected points (x-p_1)(x-p_2)...(x-p_n) + pub fn z(points: &[Field]) -> Poly { + points.iter().fold(Poly::one(), |acc, x| { + &acc * &Poly::new(vec![-x, Field::::one()]) + }) + } + + /// Evals the polinomial at the desired point + pub fn eval(&self, x: &Field) -> Field { + let mut x_pow = Field::::one(); + let mut y = self.0[0]; + for (i, _) in self.0.iter().enumerate().skip(1) { + x_pow *= x; + y += &(x_pow * self.0[i]); + } + y + } + + /// Evals the polinomial suplying the `x_pows` x^0, x^1, x^2 + pub fn eval_with_pows(&self, x_pow: &[Field]) -> Field { + let mut y = self.0[0]; + for (i, _) in self.0.iter().enumerate() { + y += &(x_pow[i] * self.0[i]); + } + y + } + + /// Returns the degree of the polinominal, degree(x+1) = 1 + pub fn degree(&self) -> usize { + self.0.len() - 1 + } + + /// Normalizes the coefficients, removing ending zeroes + pub fn normalize(&mut self) { + if self.0.len() > 1 && self.0[self.0.len() - 1].is_zero() { + let first_non_zero = self.0.iter().rev().position(|p| *p != Field::::zero()); + if let Some(first_non_zero) = first_non_zero { + self.0 + .resize(self.0.len() - first_non_zero, Field::::zero()); + } else { + self.0.resize(1, Field::::zero()); + } + } + } + + /// Returns if p(x)=0 + pub fn is_zero(&self) -> bool { + self.0.len() == 1 && self.0[0].is_zero() + } + + /// Sets the `i`-th coefficient to the selected `p` value + pub fn set(&mut self, i: usize, p: Field) { + if self.0.len() < i + 1 { + self.0.resize(i + 1, Field::::zero()); + } + self.0[i] = p; + self.normalize(); + } + + /// Returns the `i`-th coefficient + pub fn get(&mut self, i: usize) -> Option<&Field> { + self.0.get(i) + } +} + +impl Display for Poly { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + let mut first: bool = true; + for i in 0..=self.degree() { + if self.0[i].is_zero() && self.degree() > 1 { + continue; + } + let v = format!("{}", self.0[i]); + if !first { + write!(f, "+")?; + } + if i == 0 || v != "1" { + write!(f, "{}", v)?; + } + if i >= 1 { + write!(f, "x")?; + } + if i >= 2 { + write!(f, "^{}", i)?; + } + first = false; + } + Ok(()) + } +} + +/* main arithmetics */ +impl AddAssign<&Poly> for Poly { + fn add_assign(&mut self, rhs: &Poly) { + for n in 0..max(self.0.len(), rhs.0.len()) { + if n >= self.0.len() { + self.0.push(rhs.0[n]); + } else if n < self.0.len() && n < rhs.0.len() { + self.0[n] += rhs.0[n]; + } + } + self.normalize(); + } +} + +impl AddAssign<&Field> for Poly { + fn add_assign(&mut self, rhs: &Field) { + self.0[0] += rhs; + self.normalize(); + } +} + +impl SubAssign<&Field> for Poly { + fn sub_assign(&mut self, rhs: &Field) { + self.0[0] -= rhs; + self.normalize(); + } +} + +impl SubAssign<&Poly> for Poly { + fn sub_assign(&mut self, rhs: &Poly) { + for n in 0..max(self.0.len(), rhs.0.len()) { + if n >= self.0.len() { + self.0.push(rhs.0[n]); + } else if n < self.0.len() && n < rhs.0.len() { + self.0[n] -= &rhs.0[n]; + } + } + self.normalize(); + } +} + +impl Mul<&Poly> for &Poly { + type Output = Poly; + fn mul(self, rhs: &Poly) -> Self::Output { + let mut mul: Vec> = iter::repeat(Field::::zero()) + .take(self.0.len() + rhs.0.len() - 1) + .collect(); + for n in 0..self.0.len() { + for m in 0..rhs.0.len() { + mul[n + m] += self.0[n] * rhs.0[m]; + } + } + let mut m = Poly(mul); + m.normalize(); + m + } +} + +impl MulAssign<&Field> for Poly { + fn mul_assign(&mut self, rhs: &Field) { + if rhs.is_zero() { + *self = Poly::zero() + } else { + self.0.iter_mut().for_each(|v| *v = *v * rhs); + } + } +} + +impl Div for Poly { + type Output = (Poly, Poly); + + fn div(self, rhs: Poly) -> Self::Output { + let (mut q, mut r) = (Poly::zero(), self); + while !r.is_zero() && r.degree() >= rhs.degree() { + let lead_r = r.0[r.0.len() - 1]; + let lead_d = rhs.0[rhs.0.len() - 1]; + let mut t = Poly::zero(); + t.set(r.0.len() - rhs.0.len(), lead_r * lead_d.inv().unwrap()); + q += &t; + r -= &(&rhs * &t); + } + q.normalize(); + r.normalize(); + (q, r) + } +} + +/* helpers */ +impl Add for Poly { + type Output = Poly; + fn add(self, rhs: Poly) -> Self::Output { + let mut v = self.clone(); + v += &rhs; + v + } +} + +impl Add> for &Poly { + type Output = Poly; + fn add(self, rhs: Poly) -> Self::Output { + let mut v = self.clone(); + v += &rhs; + v + } +} + +impl Add<&Poly> for Poly { + type Output = Poly; + fn add(mut self, rhs: &Poly) -> Self::Output { + self += rhs; + self + } +} + +impl Add<&Poly> for &Poly { + type Output = Poly; + fn add(self, rhs: &Poly) -> Self::Output { + let mut v = self.clone(); + v += rhs; + v + } +} + +impl Add<&Field> for Poly { + type Output = Poly; + fn add(self, rhs: &Field) -> Self::Output { + let mut cloned = self.clone(); + cloned += rhs; + cloned + } +} + +impl Add> for Poly { + type Output = Poly; + fn add(self, rhs: Field) -> Self::Output { + let mut cloned = self.clone(); + cloned += &rhs; + cloned + } +} + +impl Sub> for Poly { + type Output = Poly; + fn sub(self, rhs: Field) -> Self::Output { + let mut cloned = self.clone(); + cloned -= &rhs; + cloned + } +} + +impl Add> for &Poly { + type Output = Poly; + fn add(self, rhs: Field) -> Self::Output { + let mut cloned = self.clone(); + cloned += &rhs; + cloned + } +} + +impl Sub> for Poly { + type Output = Poly; + fn sub(mut self, rhs: Poly) -> Self::Output { + self -= &rhs; + self + } +} + +impl Mul<&Poly> for Poly { + type Output = Poly; + fn mul(self, rhs: &Poly) -> Self::Output { + &self * rhs + } +} + +impl Mul> for &Poly { + type Output = Poly; + fn mul(self, rhs: Poly) -> Self::Output { + self * &rhs + } +} + +impl Mul> for Poly { + type Output = Poly; + fn mul(self, rhs: Poly) -> Self::Output { + self * &rhs + } +} + +impl Mul<&Field> for &Poly { + type Output = Poly; + fn mul(self, rhs: &Field) -> Self::Output { + let mut m = self.clone(); + m *= rhs; + m + } +} + +impl Mul<&Field> for Poly { + type Output = Poly; + fn mul(self, rhs: &Field) -> Self::Output { + &self * rhs + } +} + +impl Mul> for Poly { + type Output = Poly; + fn mul(self, rhs: Field) -> Self::Output { + self * &rhs + } +} + +impl Mul> for &Poly { + type Output = Poly; + fn mul(self, rhs: Field) -> Self::Output { + self * &rhs + } +} + +#[cfg(test)] +mod tests { + use super::*; + type F = Field<15485863>; + type P = Poly<15485863>; + + #[test] + fn test_poly_add() { + let mut p246 = P::from(&[1, 2, 3]); + p246 += &P::from(&[1, 2, 3]); + assert_eq!(p246, P::from(&[2, 4, 6])); + + let mut p24645 = P::from(&[1, 2, 3]); + p24645 += &P::from(&[1, 2, 3, 4, 5]); + assert_eq!(p24645, P::from(&[2, 4, 6, 4, 5])); + + let mut p24646 = P::from(&[1, 2, 3, 4, 6]); + p24646 += &P::from(&[1, 2, 3]); + assert_eq!(p24646, Poly::from(&[2, 4, 6, 4, 6])); + } + + #[test] + fn test_poly_sub() { + let mut p0 = P::from(&[1, 2, 3]); + p0 -= &P::from(&[1, 2, 3]); + assert_eq!(p0, P::from(&[0])); + + let mut p003 = P::from(&[1, 2, 3]); + p003 -= &P::from(&[1, 2]); + assert_eq!(p003, P::from(&[0, 0, 3])); + } + + #[test] + fn test_poly_mul() { + assert_eq!( + &P::from(&[5, 0, 10, 6]) * &P::from(&[1, 2, 4]), + P::from(&[5, 10, 30, 26, 52, 24]) + ); + } + + #[test] + fn test_poly_div() { + fn do_test(n: P, d: P) { + let (q, r) = n.clone() / d.clone(); + let mut n2 = &q * &d; + n2 += &r; + assert_eq!(n, n2); + } + + do_test(P::from(&[1]), P::from(&[1, 1])); + do_test(P::from(&[1, 1]), P::from(&[1, 1])); + do_test(P::from(&[1, 2, 1]), P::from(&[1, 1])); + do_test(P::from(&[1, 2, 1, 2, 5, 8, 1, 9]), P::from(&[1, 1, 5, 4])); + } + + #[test] + fn test_poly_print() { + assert_eq!("1+2x+x^2", format!("{}", P::from(&[1, 2, 1]))); + assert_eq!("1+x^2", format!("{}", P::from(&[1, 0, 1]))); + assert_eq!("x^2", format!("{}", P::from(&[0, 0, 1]))); + assert_eq!("2x^2", format!("{}", P::from(&[0, 0, 2]))); + } + + #[test] + fn test_poly_lagrange_multi() { + let points = vec![ + (F::from(1), F::from(2)), + (F::from(5), F::from(7)), + (F::from(7), F::from(9)), + (F::from(3), F::from(1)), + ]; + let l = Poly::lagrange(&points); + points.iter().for_each(|p| assert_eq!(l.eval(&p.0), p.1)); + } + #[test] + fn test_poly_z() { + assert_eq!( + P::z(&vec![F::from(1), F::from(5)]), + P::from(&[5, -6, 1]) // f(x) = (x-1)(x-5) = x^2-6x+5 + ); + } + #[test] + fn test_poly_eval() { + // check that (x^2+2x+1)(2) = 9 + assert_eq!(P::from(&[1, 2, 1]).eval(&F::from(2)), F::from(9)); + } + #[test] + fn test_poly_normalize() { + let mut p1 = P::from(&[1, 0, 0, 0]); + p1.normalize(); + assert_eq!(p1, P::from(&[1])); + } +} diff --git a/src/prover.rs b/src/prover.rs new file mode 100644 index 0000000..bf4a0ec --- /dev/null +++ b/src/prover.rs @@ -0,0 +1,596 @@ +#![allow(dead_code, unused_imports)] + +use super::constrains::*; +use super::ec::{g1f, g2f, pairing, G1Point, G2Point}; +use super::field::Field; +use super::matrix::Matrix; +use super::plonk::{f101, f17, p17, F101, F17, P17}; +use super::poly::Poly; + +pub struct SRS { + pub g1s: Vec, + pub g2_1: G2Point, + pub g2_s: G2Point, +} + +impl SRS { + pub fn create(s: F101, n: usize, g1: G1Point, g2: G2Point) -> SRS { + let mut g1s = Vec::new(); + let mut s_pow = s; + g1s.push(g1); + for _ in 0..n { + g1s.push(g1 * s_pow); + s_pow = s_pow * s; + } + Self { + g1s, + g2_1: g2, + g2_s: g2 * s, + } + } + + // evaluate a polinomial at secret point s using SRS G1s + pub fn eval_at_s(&self, vs: &Poly<17>) -> G1Point { + vs.coeffs() + .iter() + .enumerate() + .fold(G1Point::inf(), |acc, (n, v)| { + acc + self.g1s[n] * v.rebase::<101>() + }) + } +} + +#[derive(Debug, PartialEq)] +pub struct Proof { + a_s: G1Point, + b_s: G1Point, + c_s: G1Point, + z_s: G1Point, + t_lo_s: G1Point, + t_mid_s: G1Point, + t_hi_s: G1Point, + w_z_s: G1Point, + w_z_omega_s: G1Point, + a_z: Field<17>, + b_z: Field<17>, + c_z: Field<17>, + s_sigma_1_z: Field<17>, + s_sigma_2_z: Field<17>, + r_z: Field<17>, + z_omega_z: Field<17>, +} + +pub struct Challange { + pub alpha: F17, + pub beta: F17, + pub gamma: F17, + pub z: F17, + pub v: F17, +} + +struct Plonk { + srs: SRS, + omega: F17, + h: [F17; 4], + h_pows_inv: Matrix<17>, + k1: F17, + k2: F17, + k1_h: Vec, + k2_h: Vec, + z_h_x: P17, // Zh(x) = x^4 - 1 +} + +impl Plonk { + pub fn new(srs: SRS) -> Plonk { + let omega = f17(4); + + // roots of unity for x^omega = 1 + let h = [f17(1), f17(4), f17(16), f17(13)]; + + let mut h_pows = Matrix::<17>::zero(4, 4); + for c in 0..h_pows.cols() { + for r in 0..h_pows.rows() { + h_pows[(r, c)] = h[r].pow(c as u64); + } + } + let h_pows_inv = h_pows.inv(); + + let (k1, k2) = (f17(2), f17(3)); + assert!(!h.contains(&k1)); + assert!(!h.contains(&k2)); + + let k1_h: Vec = h.iter().map(|r| *r * k1).collect(); + let k2_h: Vec = h.iter().map(|r| *r * k2).collect(); + + let z_h_x = p17(&[-1, 0, 0, 0, 1]); // Zh(x) = x^4 - 1 + + Plonk { + srs, + omega, + h, + h_pows_inv, + k1, + k2, + k1_h, + k2_h, + z_h_x, + } + } + fn interpolate(&self, vv: &[Field<17>]) -> P17 { + (&self.h_pows_inv * Matrix::<17>::new(&vv, vv.len(), 1)).into_poly() + } + fn copyc_to_roots(&self, c: &[CopyOf]) -> Vec { + c.iter() + .map(|c| match c { + CopyOf::A(n) => self.h[n - 1], + CopyOf::B(n) => self.k1_h[n - 1], + CopyOf::C(n) => self.k2_h[n - 1], + }) + .collect::>() + } + + pub fn prove( + &self, + constrains: &Constrains, + assigments: &Assigments, + challange: &Challange, + rand: [F17; 9], + ) -> Proof { + // check that the constrains satisfies the assigments + assert!(constrains.satisfies(&assigments)); + + let Challange { + alpha, + beta, + gamma, + z, + v, + } = challange; + + let n = constrains.c_a.len() as u64; + + // now I have a generator g=(1,2) on Field::, that generates a subgroup F17 + // we need at least nth-roots-of-unity from F17 + // fortunately =4 and there are 4 elements in F17 that x^4 = 1 + // roots_of_unity are H={1,4,16,13} + // create sigmas, mappings from the copy constrains to roots of unity elements + // --------------------------------------------------------------------------- + + let sigma_1 = self.copyc_to_roots(&constrains.c_a); + let sigma_2 = self.copyc_to_roots(&constrains.c_b); + let sigma_3 = self.copyc_to_roots(&constrains.c_c); + + // prepare vectors as polys + // --------------------------------------------------------------------------- + + let f_a_x = self.interpolate(&assigments.a); + let f_b_x = self.interpolate(&assigments.b); + let f_c_x = self.interpolate(&assigments.c); + let q_o_x = self.interpolate(&constrains.q_o); + let q_m_x = self.interpolate(&constrains.q_m); + let q_l_x = self.interpolate(&constrains.q_l); + let q_r_x = self.interpolate(&constrains.q_r); + let q_c_x = self.interpolate(&constrains.q_c); + let s_sigma_1 = self.interpolate(&sigma_1); + let s_sigma_2 = self.interpolate(&sigma_2); + let s_sigma_3 = self.interpolate(&sigma_3); + + // round 1 + // --------------------------------------------------------------------------- + + let (b1, b2, b3, b4, b5, b6) = (rand[0], rand[1], rand[2], rand[3], rand[4], rand[5]); + + let a_x = P17::new(vec![b2, b1]) * &self.z_h_x + f_a_x; + let b_x = P17::new(vec![b4, b3]) * &self.z_h_x + f_b_x; + let c_x = P17::new(vec![b6, b5]) * &self.z_h_x + f_c_x; + + let a_s = self.srs.eval_at_s(&a_x); + let b_s = self.srs.eval_at_s(&b_x); + let c_s = self.srs.eval_at_s(&c_x); + + // round 2 + // --------------------------------------------------------------------------- + + let (b7, b8, b9) = (rand[6], rand[7], rand[8]); + + let mut acc = vec![f17(1)]; + for i in 1..n as usize { + let a = assigments.a[i - 1]; + let b = assigments.b[i - 1]; + let c = assigments.c[i - 1]; + let omega_pow = self.omega.pow((i as u64) - 1); + let dend = (a + beta * omega_pow + gamma) + * (b + beta * self.k1 * omega_pow + gamma) + * (c + beta * self.k2 * omega_pow + gamma); + let dsor = (a + beta * s_sigma_1.eval(&omega_pow) + gamma) + * (b + beta * s_sigma_2.eval(&omega_pow) + gamma) + * (c + beta * s_sigma_3.eval(&omega_pow) + gamma); + + let v = acc[i - 1] * (dend / dsor).unwrap(); + acc.push(v); + } + + let acc_x = self.interpolate(&acc); + let z_x = P17::new(vec![b9, b8, b7]) * &self.z_h_x + acc_x; + let z_s = self.srs.eval_at_s(&z_x); + + // round 3 + // --------------------------------------------------------------------------- + + let l_1_x = self.interpolate(&[f17(1), f17(0), f17(0), f17(0)]); + let p_i_x = f17(0).as_poly(); // Public input + + let a_x_b_x_q_m_x = (&a_x * &b_x) * &q_m_x; + let a_x_q_l_x = &a_x * &q_l_x; + let b_x_q_r_x = &b_x * &q_r_x; + let c_x_q_o_x = &c_x * &q_o_x; + let alpha_a_x_beta_x_gamma = &(&a_x + &Poly::new(vec![*gamma, *beta])) * alpha; + let b_x_beta_k1_x_gamma = &b_x + &Poly::new(vec![*gamma, beta * self.k1]); + let c_x_beta_k2_x_gamma = &c_x + &Poly::new(vec![*gamma, beta * self.k2]); + let z_omega_x = Poly::new( + z_x.coeffs() + .iter() + .enumerate() + .map(|(n, c)| c * &self.omega.pow(n as u64)) + .collect::>(), + ); + let alpha_a_x_beta_s_sigma1_x_gamma = (&a_x + &s_sigma_1 * beta + gamma) * alpha; + let b_x_beta_s_sigma2_x_gamma = &b_x + &s_sigma_2 * beta + gamma; + let c_x_beta_s_sigma3_x_gamma = &c_x + &s_sigma_3 * beta + gamma; + let alpha_2_z_x_1_l_1_x = ((&z_x + (-f17(1)).as_poly()) * alpha.pow(2)) * &l_1_x; + + let t_1_z_h = a_x_b_x_q_m_x + a_x_q_l_x + b_x_q_r_x + c_x_q_o_x + p_i_x + &q_c_x; + let t_2_z_h = alpha_a_x_beta_x_gamma * b_x_beta_k1_x_gamma * c_x_beta_k2_x_gamma * &z_x; + let t_3_z_h = alpha_a_x_beta_s_sigma1_x_gamma + * b_x_beta_s_sigma2_x_gamma + * c_x_beta_s_sigma3_x_gamma + * &z_omega_x; + + let t_4_z_h = alpha_2_z_x_1_l_1_x; + + let (t_x, rem) = (t_1_z_h + t_2_z_h - t_3_z_h + t_4_z_h) / self.z_h_x.clone(); + assert_eq!(rem, Poly::zero()); + + let t_hi_x = Poly::new(t_x.coeffs()[2 * 6..3 * 6].to_owned()); + let t_mid_x = Poly::new(t_x.coeffs()[1 * 6..2 * 6].to_owned()); + let t_lo_x = Poly::new(t_x.coeffs()[0 * 6..1 * 6].to_owned()); + + let t_hi_s = self.srs.eval_at_s(&t_hi_x); + let t_mid_s = self.srs.eval_at_s(&t_mid_x); + let t_lo_s = self.srs.eval_at_s(&t_lo_x); + + // round 4 + // --------------------------------------------------------------------------- + + let a_z = a_x.eval(&z); + let b_z = b_x.eval(&z); + let c_z = c_x.eval(&z); + let s_sigma_1_z = s_sigma_1.eval(&z); + let s_sigma_2_z = s_sigma_2.eval(&z); + let t_z = t_x.eval(&z); + let z_omega_z = z_omega_x.eval(&z); + + let a_z_b_z_q_m_x = &q_m_x * &a_z * &b_z; + + let a_z_q_l_x = &q_l_x * &a_z; + let b_z_q_r_x = &q_r_x * &b_z; + let c_z_q_o_x = &q_o_x * &c_z; + + let r_1_x = a_z_b_z_q_m_x + a_z_q_l_x + b_z_q_r_x + c_z_q_o_x + q_c_x; + let r_2_x = &z_x + * ((a_z + beta * z + gamma) + * (b_z + beta * self.k1 * z + gamma) + * (c_z + beta * self.k2 * z + gamma) + * alpha); + let r_3_x = &z_x + * ((a_z + beta * s_sigma_1_z + gamma) + * (b_z + beta * s_sigma_2_z + gamma) + * (beta * z_omega_z * s_sigma_3) + * alpha); + let r_4_x = &z_x * l_1_x.eval(&z) * alpha.pow(2); + + let r_x = r_1_x + r_2_x + r_3_x + r_4_x; + let r_z = r_x.eval(&z); + + // round 5 + // --------------------------------------------------------------------------- + + let v = v; + let w_z_x = (t_lo_x + t_mid_x * z.pow(n + 2) + t_hi_x * z.pow(2 * n + 4) - t_z) + + v * (r_x - r_z) + + v.pow(2) * (a_x - a_z) + + v.pow(3) * (b_x - b_z) + + v.pow(4) * (c_x - c_z) + + v.pow(5) * (s_sigma_1 - s_sigma_1_z) + + v.pow(6) * (s_sigma_2 - s_sigma_2_z); + let (w_z_x, rem) = w_z_x / Poly::new(vec![-z, f17(1)]); + assert_eq!(rem, Poly::zero()); + + let (w_z_omega_x, rem) = (z_x - z_omega_z) / Poly::new(vec![-z * self.omega, f17(1)]); + assert_eq!(rem, Poly::zero()); + + let w_z_s = self.srs.eval_at_s(&w_z_x); + let w_z_omega_s = self.srs.eval_at_s(&w_z_omega_x); + + Proof { + a_s, + b_s, + c_s, + z_s, + t_lo_s, + t_mid_s, + t_hi_s, + w_z_s, + w_z_omega_s, + a_z, + b_z, + c_z, + s_sigma_1_z, + s_sigma_2_z, + r_z, + z_omega_z, + } + } + + fn verify( + &self, + constrains: &Constrains, + proof: &Proof, + challange: &Challange, + rand: [F17; 1], + ) -> bool { + let Proof { + a_s, + b_s, + c_s, + z_s, + t_lo_s, + t_mid_s, + t_hi_s, + w_z_s, + w_z_omega_s, + a_z, + b_z, + c_z, + s_sigma_1_z, + s_sigma_2_z, + r_z, + z_omega_z, + } = proof; + + let Challange { + alpha, + beta, + gamma, + z, + v, + } = challange; + + // verifier preprocessing + // --------------------------------------------------------------------------- + let sigma_1 = self.copyc_to_roots(&constrains.c_a); + let sigma_2 = self.copyc_to_roots(&constrains.c_b); + let sigma_3 = self.copyc_to_roots(&constrains.c_c); + + let q_m_s = self.srs.eval_at_s(&self.interpolate(&constrains.q_m)); + let q_l_s = self.srs.eval_at_s(&self.interpolate(&constrains.q_l)); + let q_r_s = self.srs.eval_at_s(&self.interpolate(&constrains.q_r)); + let q_o_s = self.srs.eval_at_s(&self.interpolate(&constrains.q_o)); + let q_c_s = self.srs.eval_at_s(&self.interpolate(&constrains.q_c)); + let sigma_1_s = self.srs.eval_at_s(&self.interpolate(&sigma_1)); + let sigma_2_s = self.srs.eval_at_s(&self.interpolate(&sigma_2)); + let sigma_3_s = self.srs.eval_at_s(&self.interpolate(&sigma_3)); + + let u = rand[0]; + + // Step 1. Validate proof points in G1 + + if !a_s.in_curve() + || !b_s.in_curve() + || !c_s.in_curve() + || !z_s.in_curve() + || !t_lo_s.in_curve() + || !t_mid_s.in_curve() + || !t_hi_s.in_curve() + || !w_z_s.in_curve() + || !w_z_omega_s.in_curve() + { + return false; + } + + // Step 2. Validate proof fields in F17 + + if !a_z.in_field() + || !b_z.in_field() + || !c_z.in_field() + || !s_sigma_1_z.in_field() + || !s_sigma_2_z.in_field() + || !r_z.in_field() + || !z_omega_z.in_field() + { + return false; + } + + // Step 3. We do not have public inputs, nothing to do + + // Step 4. Evaluate z_h at z + + let z_h_z = self.z_h_x.eval(&z); + + // Step 5. Evaluate lagrange on z + + let l_1_z = self.interpolate(&[f17(1), f17(0), f17(0), f17(0)]).eval(&z); + + // Step 6. We do not have public inputs, nothing to do + + let p_i_z = F17::zero(); + + // Step 7. Compute quotient polinomial evaluation + + let a_z_beta_s_sigma_1_z_gamma = a_z + beta * s_sigma_1_z + gamma; + let b_z_beta_s_sigma_2_z_gamma = b_z + beta * s_sigma_2_z + gamma; + let c_z_gamma = c_z + gamma; + let l_1_z_alpha_2 = l_1_z * alpha.pow(2); + let t_z = ((r_z + p_i_z + - (a_z_beta_s_sigma_1_z_gamma * b_z_beta_s_sigma_2_z_gamma * c_z_gamma * z_omega_z) + - l_1_z_alpha_2) + / z_h_z) + .unwrap(); + + // Step 8. Compute the first part of batched polinomial commitment + + let d_1_s = q_m_s * (a_z * b_z * v).rebase::<101>() + + q_l_s * (a_z * v).rebase::<101>() + + q_r_s * (b_z * v).rebase::<101>() + + q_o_s * (c_z * v).rebase::<101>() + + q_c_s * (v).rebase::<101>(); + + let d_2_s = *z_s + * ((a_z + beta * z + gamma) + * (b_z + beta * self.k1 * z + gamma) + * (c_z + beta * self.k2 * z + gamma) + * alpha + * v + + l_1_z * alpha.pow(2) * v + + u) + .rebase::<101>(); + + let d_3_s = sigma_3_s + * ((a_z + beta * s_sigma_1_z + gamma) + * (b_z + beta * s_sigma_2_z + gamma) + * alpha + * v + * beta + * z_omega_z) + .rebase::<101>(); + + let d_s = d_1_s + d_2_s + -d_3_s; + + // Step 9. Compute full batched polinomial commitment + + let n = constrains.c_a.len() as u64; + + let f_s = t_lo_s.to_owned() + + t_mid_s.to_owned() * z.pow(n + 2).rebase::<101>() + + t_hi_s.to_owned() * z.pow(2 * n + 4).rebase::<101>() + + d_s + + a_s.to_owned() * v.pow(2).rebase::<101>() + + b_s.to_owned() * v.pow(3).rebase::<101>() + + c_s.to_owned() * v.pow(4).rebase::<101>() + + sigma_1_s.to_owned() * v.pow(5).rebase::<101>() + + sigma_2_s.to_owned() * v.pow(6).rebase::<101>(); + + // Step 10. Compute group encoded batch evaluation [E] + + let e_s = self.srs.eval_at_s(&p17(&[1])) + * (t_z + + v * r_z + + v.pow(2) * a_z + + v.pow(3) * b_z + + v.pow(4) * c_z + + v.pow(5) * s_sigma_1_z + + v.pow(6) * s_sigma_2_z + + u * z_omega_z) + .rebase::<101>(); + + // Step 11. Batch validate all equations + + let e_1_q1 = *w_z_s + *w_z_omega_s * u.rebase::<101>(); + let e_1_q2 = self.srs.g2_s; + let e_2_q1 = *w_z_s * z.rebase::<101>() + + *w_z_omega_s * (u * z * self.omega).rebase::<101>() + + f_s + + -e_s; + let e_2_q2 = self.srs.g2_1; + + let e_1 = pairing(e_1_q1, e_1_q2); + let e_2 = pairing(e_2_q1, e_2_q2); + + println!("e1({},{}) = {}", e_1_q1, e_1_q2, e_1); + println!("e2({},{}) = {}", e_2_q1, e_2_q2, e_2); + + e_1 == e_2 + } +} + +#[cfg(test)] +mod tests { + use super::*; + #[test] + fn test_plonk_gen_proof() { + // create the trusted setup + let s = 2; // the toxic waste + let srs = SRS::create(f101(s), 6, g1f(1, 2), g2f(36, 31)); + + let plonk = Plonk::new(srs); + + // constrains and assigments + let constrains = Constrains::new( + &[ + Gate::mul_a_b(), + Gate::mul_a_b(), + Gate::mul_a_b(), + Gate::sum_a_b(), + ], + ( + vec![CopyOf::B(1), CopyOf::B(2), CopyOf::B(3), CopyOf::C(1)], + vec![CopyOf::A(1), CopyOf::A(2), CopyOf::A(3), CopyOf::C(2)], + vec![CopyOf::A(4), CopyOf::B(4), CopyOf::C(4), CopyOf::C(3)], + ), + ); + + let assigments = Assigments::new(&[ + Assigment::new(Field::from(3), Field::from(3), Field::from(9)), + Assigment::new(Field::from(4), Field::from(4), Field::from(16)), + Assigment::new(Field::from(5), Field::from(5), Field::from(25)), + Assigment::new(Field::from(9), Field::from(16), Field::from(25)), + ]); + + // random numbers (the b's) + let rand = [ + f17(7), + f17(4), + f17(11), + f17(12), + f17(16), + f17(2), + f17(14), + f17(11), + f17(7), + ]; + + // values that are sent from the verifier to the prover + let challange = Challange { + alpha: f17(15), + beta: f17(12), + gamma: f17(13), + z: f17(5), + v: f17(12), + }; + + let proof = plonk.prove(&constrains, &assigments, &challange, rand); + + let expected = Proof { + a_s: g1f(91, 66), + b_s: g1f(26, 45), + c_s: g1f(91, 35), + z_s: g1f(32, 59), + t_lo_s: g1f(12, 32), + t_mid_s: g1f(26, 45), + t_hi_s: g1f(91, 66), + w_z_s: g1f(91, 35), + w_z_omega_s: g1f(65, 98), + a_z: f17(15), + b_z: f17(13), + c_z: f17(5), + s_sigma_1_z: f17(1), + s_sigma_2_z: f17(12), + r_z: f17(15), + z_omega_z: f17(15), + }; + assert_eq!(proof, expected); + + let rand = [f17(4)]; + assert!(plonk.verify(&constrains, &proof, &challange, rand)); + } +}