This commit is contained in:
adria0
2021-09-17 18:03:40 +02:00
commit 21c2e2f3cd
12 changed files with 2081 additions and 0 deletions

10
.gitignore vendored Normal file
View File

@@ -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

8
Cargo.toml Normal file
View File

@@ -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]

21
LICENSE Normal file
View File

@@ -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.

6
README.md Normal file
View File

@@ -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 <img src="https://render.githubusercontent.com/render/math?math=l_{16P,P}=x%2B15"> where the correct value seems to be <img src="https://render.githubusercontent.com/render/math?math=l_{16P,P}=x%2B100">, this also affects the pairing, that is <img src="https://render.githubusercontent.com/render/math?math=93%2b76u"> instead <img src="https://render.githubusercontent.com/render/math?math=97%2b89u">

167
src/constrains.rs Normal file
View File

@@ -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<F17>,
pub q_r: Vec<F17>,
pub q_o: Vec<F17>,
pub q_m: Vec<F17>,
pub q_c: Vec<F17>,
pub c_a: Vec<CopyOf>,
pub c_b: Vec<CopyOf>,
pub c_c: Vec<CopyOf>,
}
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<F17>,
pub b: Vec<F17>,
pub c: Vec<F17>,
}
impl Constrains {
pub fn new(gates: &[Gate], copy_constrains: (Vec<CopyOf>, Vec<CopyOf>, Vec<CopyOf>)) -> 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()
}
}

312
src/ec.rs Normal file
View File

@@ -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<F101> 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<F101> 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<G2Point> 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));
}
}

243
src/field.rs Normal file
View File

@@ -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 -= &quotient * &r;
std::mem::swap(&mut old_r, &mut r);
old_s -= &quotient * &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<const M: u64>(u64);
#[allow(non_snake_case)]
pub fn Fi64<const M: u64>(n: i64) -> Field<M> {
if n < 0 {
-Field::from(-n as u64)
} else {
Field::from(n as u64)
}
}
impl<const M: u64> Field<M> {
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<const M2: u64>(&self) -> Field<M2> {
Field::<M2>::from(self.0)
}
pub fn inv(&self) -> Option<Self> {
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<M> {
Poly::new(vec![*self])
}
}
impl<const M: u64> Display for Field<M> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
impl<const M: u64> Add for Field<M> {
type Output = Field<M>;
fn add(self, rhs: Self) -> Self::Output {
Field((self.0 + rhs.0) % M)
}
}
impl<const M: u64> Add<&Field<M>> for Field<M> {
type Output = Field<M>;
fn add(self, rhs: &Field<M>) -> Self::Output {
Field((self.0 + rhs.0) % M)
}
}
impl<const M: u64> Add<Field<M>> for &Field<M> {
type Output = Field<M>;
fn add(self, rhs: Field<M>) -> Self::Output {
Field((self.0 + rhs.0) % M)
}
}
impl<const M: u64> Add<&Field<M>> for &Field<M> {
type Output = Field<M>;
fn add(self, rhs: &Field<M>) -> Self::Output {
Field((self.0 + rhs.0) % M)
}
}
impl<const M: u64> AddAssign<&Field<M>> for Field<M> {
fn add_assign(&mut self, rhs: &Field<M>) {
self.0 = (self.0 + rhs.0) % M;
}
}
impl<const M: u64> AddAssign<Field<M>> for Field<M> {
fn add_assign(&mut self, rhs: Field<M>) {
self.0 = (self.0 + rhs.0) % M;
}
}
impl<const M: u64> Sub for Field<M> {
type Output = Field<M>;
fn sub(self, rhs: Self) -> Self::Output {
self + -rhs
}
}
impl<const M: u64> SubAssign<&Field<M>> for Field<M> {
fn sub_assign(&mut self, rhs: &Field<M>) {
*self += -rhs;
}
}
impl<const M: u64> Neg for Field<M> {
type Output = Field<M>;
fn neg(self) -> Self::Output {
Field((M - self.0) % M)
}
}
impl<const M: u64> Neg for &Field<M> {
type Output = Field<M>;
fn neg(self) -> Self::Output {
Field((M - self.0) % M)
}
}
impl<const M: u64> Mul for Field<M> {
type Output = Field<M>;
fn mul(self, rhs: Self) -> Self::Output {
Field((self.0 * rhs.0) % M)
}
}
impl<const M: u64> Mul<&Field<M>> for &Field<M> {
type Output = Field<M>;
fn mul(self, rhs: &Field<M>) -> Self::Output {
Field((self.0 * rhs.0) % M)
}
}
impl<const M: u64> Mul<&Field<M>> for Field<M> {
type Output = Field<M>;
fn mul(self, rhs: &Field<M>) -> Self::Output {
Field((self.0 * rhs.0) % M)
}
}
impl<const M: u64> Mul<Field<M>> for &Field<M> {
type Output = Field<M>;
fn mul(self, rhs: Field<M>) -> Self::Output {
Field((self.0 * rhs.0) % M)
}
}
impl<const M: u64> Mul<Poly<M>> for &Field<M> {
type Output = Poly<M>;
fn mul(self, rhs: Poly<M>) -> Self::Output {
rhs * self
}
}
impl<const M: u64> Mul<Poly<M>> for Field<M> {
type Output = Poly<M>;
fn mul(self, rhs: Poly<M>) -> Self::Output {
rhs * self
}
}
impl<const M: u64> MulAssign<&Field<M>> for Field<M> {
fn mul_assign(&mut self, rhs: &Field<M>) {
self.0 = (self.0 * rhs.0) % M;
}
}
impl<const M: u64> Div for Field<M> {
type Output = Option<Field<M>>;
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));
}
}

7
src/lib.rs Normal file
View File

@@ -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;

215
src/matrix.rs Normal file
View File

@@ -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<const M: u64> {
m: usize, // rows
n: usize, // cols
v: Vec<Field<M>>,
}
impl<const M: u64> Matrix<M> {
pub fn zero(m: usize, n: usize) -> Self {
let mut v = Vec::new();
v.resize(n * m, Field::<M>::from(0));
Self { m, n, v }
}
pub fn new(v: &[Field<M>], 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::<M>::from(*x)).collect(),
}
}
pub fn cols(&self) -> usize {
self.n
}
pub fn rows(&self) -> usize {
self.m
}
pub fn inv(&self) -> Matrix<M> {
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<M> {
assert_eq!(1, self.n);
Poly::new(self.v)
}
}
impl<const M: u64> Index<(usize, usize)> for Matrix<M> {
type Output = Field<M>;
// 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<const M: u64> IndexMut<(usize, usize)> for Matrix<M> {
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<const M: u64> Mul<Matrix<M>> for Matrix<M> {
type Output = Matrix<M>;
fn mul(self, rhs: Matrix<M>) -> Self::Output {
&self * rhs
}
}
impl<const M: u64> Mul<Matrix<M>> for &Matrix<M> {
type Output = Matrix<M>;
fn mul(self, rhs: Matrix<M>) -> 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<const M: u64> Add<Matrix<M>> for Matrix<M> {
type Output = Matrix<M>;
fn add(self, rhs: Matrix<M>) -> 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<const M: u64> Display for Matrix<M> {
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());
}
}

21
src/plonk.rs Normal file
View File

@@ -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)
}

475
src/poly.rs Normal file
View File

@@ -0,0 +1,475 @@
//! This module provides an implementation of polinomials over bls12_381::Field<M>
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<M> coeffs
#[derive(Clone, Debug, PartialEq)]
pub struct Poly<const M: u64>(Vec<Field<M>>);
impl<const M: u64> Poly<M> {
/// 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<Field<M>>) -> 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::<M>::from(*n as u64)
} else {
-Field::<M>::from(-*n as u64)
}
})
.collect::<Vec<Field<M>>>(),
)
}
pub fn coeffs(&self) -> &[Field<M>] {
&self.0
}
/// Returns p(x)=0
pub fn zero() -> Self {
Poly(vec![Field::<M>::zero()])
}
/// Returns p(x)=1
pub fn one() -> Self {
Poly(vec![Field::<M>::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<M>, Field<M>)]) -> 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<M>]) -> Poly<M> {
points.iter().fold(Poly::one(), |acc, x| {
&acc * &Poly::new(vec![-x, Field::<M>::one()])
})
}
/// Evals the polinomial at the desired point
pub fn eval(&self, x: &Field<M>) -> Field<M> {
let mut x_pow = Field::<M>::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<M>]) -> Field<M> {
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::<M>::zero());
if let Some(first_non_zero) = first_non_zero {
self.0
.resize(self.0.len() - first_non_zero, Field::<M>::zero());
} else {
self.0.resize(1, Field::<M>::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<M>) {
if self.0.len() < i + 1 {
self.0.resize(i + 1, Field::<M>::zero());
}
self.0[i] = p;
self.normalize();
}
/// Returns the `i`-th coefficient
pub fn get(&mut self, i: usize) -> Option<&Field<M>> {
self.0.get(i)
}
}
impl<const M: u64> Display for Poly<M> {
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<const M: u64> AddAssign<&Poly<M>> for Poly<M> {
fn add_assign(&mut self, rhs: &Poly<M>) {
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<const M: u64> AddAssign<&Field<M>> for Poly<M> {
fn add_assign(&mut self, rhs: &Field<M>) {
self.0[0] += rhs;
self.normalize();
}
}
impl<const M: u64> SubAssign<&Field<M>> for Poly<M> {
fn sub_assign(&mut self, rhs: &Field<M>) {
self.0[0] -= rhs;
self.normalize();
}
}
impl<const M: u64> SubAssign<&Poly<M>> for Poly<M> {
fn sub_assign(&mut self, rhs: &Poly<M>) {
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<const M: u64> Mul<&Poly<M>> for &Poly<M> {
type Output = Poly<M>;
fn mul(self, rhs: &Poly<M>) -> Self::Output {
let mut mul: Vec<Field<M>> = iter::repeat(Field::<M>::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<const M: u64> MulAssign<&Field<M>> for Poly<M> {
fn mul_assign(&mut self, rhs: &Field<M>) {
if rhs.is_zero() {
*self = Poly::zero()
} else {
self.0.iter_mut().for_each(|v| *v = *v * rhs);
}
}
}
impl<const M: u64> Div for Poly<M> {
type Output = (Poly<M>, Poly<M>);
fn div(self, rhs: Poly<M>) -> 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<const M: u64> Add for Poly<M> {
type Output = Poly<M>;
fn add(self, rhs: Poly<M>) -> Self::Output {
let mut v = self.clone();
v += &rhs;
v
}
}
impl<const M: u64> Add<Poly<M>> for &Poly<M> {
type Output = Poly<M>;
fn add(self, rhs: Poly<M>) -> Self::Output {
let mut v = self.clone();
v += &rhs;
v
}
}
impl<const M: u64> Add<&Poly<M>> for Poly<M> {
type Output = Poly<M>;
fn add(mut self, rhs: &Poly<M>) -> Self::Output {
self += rhs;
self
}
}
impl<const M: u64> Add<&Poly<M>> for &Poly<M> {
type Output = Poly<M>;
fn add(self, rhs: &Poly<M>) -> Self::Output {
let mut v = self.clone();
v += rhs;
v
}
}
impl<const M: u64> Add<&Field<M>> for Poly<M> {
type Output = Poly<M>;
fn add(self, rhs: &Field<M>) -> Self::Output {
let mut cloned = self.clone();
cloned += rhs;
cloned
}
}
impl<const M: u64> Add<Field<M>> for Poly<M> {
type Output = Poly<M>;
fn add(self, rhs: Field<M>) -> Self::Output {
let mut cloned = self.clone();
cloned += &rhs;
cloned
}
}
impl<const M: u64> Sub<Field<M>> for Poly<M> {
type Output = Poly<M>;
fn sub(self, rhs: Field<M>) -> Self::Output {
let mut cloned = self.clone();
cloned -= &rhs;
cloned
}
}
impl<const M: u64> Add<Field<M>> for &Poly<M> {
type Output = Poly<M>;
fn add(self, rhs: Field<M>) -> Self::Output {
let mut cloned = self.clone();
cloned += &rhs;
cloned
}
}
impl<const M: u64> Sub<Poly<M>> for Poly<M> {
type Output = Poly<M>;
fn sub(mut self, rhs: Poly<M>) -> Self::Output {
self -= &rhs;
self
}
}
impl<const M: u64> Mul<&Poly<M>> for Poly<M> {
type Output = Poly<M>;
fn mul(self, rhs: &Poly<M>) -> Self::Output {
&self * rhs
}
}
impl<const M: u64> Mul<Poly<M>> for &Poly<M> {
type Output = Poly<M>;
fn mul(self, rhs: Poly<M>) -> Self::Output {
self * &rhs
}
}
impl<const M: u64> Mul<Poly<M>> for Poly<M> {
type Output = Poly<M>;
fn mul(self, rhs: Poly<M>) -> Self::Output {
self * &rhs
}
}
impl<const M: u64> Mul<&Field<M>> for &Poly<M> {
type Output = Poly<M>;
fn mul(self, rhs: &Field<M>) -> Self::Output {
let mut m = self.clone();
m *= rhs;
m
}
}
impl<const M: u64> Mul<&Field<M>> for Poly<M> {
type Output = Poly<M>;
fn mul(self, rhs: &Field<M>) -> Self::Output {
&self * rhs
}
}
impl<const M: u64> Mul<Field<M>> for Poly<M> {
type Output = Poly<M>;
fn mul(self, rhs: Field<M>) -> Self::Output {
self * &rhs
}
}
impl<const M: u64> Mul<Field<M>> for &Poly<M> {
type Output = Poly<M>;
fn mul(self, rhs: Field<M>) -> 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]));
}
}

596
src/prover.rs Normal file
View File

@@ -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<G1Point>,
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<F17>,
k2_h: Vec<F17>,
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<F17> = h.iter().map(|r| *r * k1).collect();
let k2_h: Vec<F17> = 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<F17> {
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::<Vec<_>>()
}
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::<M>, that generates a subgroup F17
// we need at least <gates_len> nth-roots-of-unity from F17
// fortunately <gates_len>=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::<Vec<_>>(),
);
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));
}
}