mirror of
https://github.com/adria0/plonk-by-fingers.git
synced 2026-01-09 05:28:03 -05:00
cleanup
This commit is contained in:
10
.gitignore
vendored
Normal file
10
.gitignore
vendored
Normal 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
8
Cargo.toml
Normal 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
21
LICENSE
Normal 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
6
README.md
Normal 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
167
src/constrains.rs
Normal 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
312
src/ec.rs
Normal 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
243
src/field.rs
Normal 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 -= "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<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
7
src/lib.rs
Normal 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
215
src/matrix.rs
Normal 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
21
src/plonk.rs
Normal 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
475
src/poly.rs
Normal 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
596
src/prover.rs
Normal 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));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user