Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

zkhack Bounty: Sumcheck #149

Merged
merged 10 commits into from
Aug 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 28 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,26 @@
</div>

## Overview

Ronkathon is a rust implementation of a collection of cryptographic primitives. It is inspired by the common python plonkathon repository, and plonk-by-hand. We use the same curve and field as plonk-by-hand (not secure), and are working towards building everything from scratch to understand everything from first principles.

## Multivariate polynomials and sum-check

This project implements the sum-check protocol for multivariate polynomials over finite fields. The sum-check protocol is an interactive proof system where a prover convinces a verifier of the sum of a multivariate polynomial over a boolean hypercube. This implementation includes:

- A `MultiVarPolynomial` struct which represents a multivariate polynomial
- A `SumCheckProver` for generating proofs
- A `SumCheckVerifier` for verifying proofs
- A `SumCheck` struct that encapsulates the entire protocol.

Use

`cargo run --example sumcheck_ex`

to run example code.

## Primitives

- [Finite Group](src/field/group.rs)
- [Fields and Their Extensions](src/field/README.md)
- [Binary Fields](src/field/binary_towers/README.md)
Expand All @@ -28,46 +45,56 @@ Ronkathon is a rust implementation of a collection of cryptographic primitives.
- [DSL](src/compiler/README.md)

### Signatures

- [Tiny ECDSA](src/ecdsa.rs)

### Encryption

- [RSA](src/encryption/asymmetric/rsa/README.md)
- [DES](src/encryption/symmetric/des/README.md)
- [AES](src/encryption/symmetric/aes/README.md)
- [ChaCha](src/encryption/symmetric/chacha/README.md)

### Hash

- [Sha256 Hash](src/hashes/README.md)
- [Poseidon Hash](src/hashes/poseidon/README.md)

## In Progress

- [ ] Edwards curve Signatures (EdDSA)

## Resources

We have found the following resources helpful in understanding the foundational mathematics behind this implementation. After going through these, you should be able to understand the codebase

### Theoretic Resources

- [Plonk by Hand P1](https://research.metastate.dev/plonk-by-hand-part-1/)
- [Plonk by Hand P2](https://research.metastate.dev/plonk-by-hand-part-2-the-proof/)

### Code Refrences

- [Plonkathon](https://github.com/0xPARC/plonkathon/blob/main/README.md)
- [Plonky3](https://github.com/Plonky3/Plonky3)
- [py_pairing](https://github.com/ethereum/py_pairing/tree/master)
- [arkworks](https://github.com/arkworks-rs)


## Math

To see computations used in the background, go to the `math/` directory.
From there, you can run the `.sage` files in a SageMath environment.
In particular, the `math/field.sage` computes roots of unity in the `PlutoField` which is of size 101. To install sage on your machine, follow the instructions [here](https://doc.sagemath.org/html/en/installation/index.html). If you are on a Mac, you can install it via homebrew with `brew install --cask sage`.

## License

Licensed under your option of either:

- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
- MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)

## Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted
for inclusion in the work by you, as defined in the Apache-2.0 license, shall be
dual licensed as above, without any additional terms or conditions.
55 changes: 55 additions & 0 deletions examples/sumcheck_ex.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// File: examples/sum_check_demo.rs

use ronkathon::{
algebra::field::prime::PlutoBaseField, multi_var_poly::MultiVarPolynomial, sumcheck::SumCheck,
};

type F = PlutoBaseField;

fn create_demo_polynomial() -> MultiVarPolynomial<F> {
// Create the polynomial:
// 3 x^2 y^2 z^2 + 2x^2 y + 5x^2 z^2 + 4yz + 6x + 1
let coordinates = vec![
vec![0, 0, 0], // Constant term
vec![1, 0, 0], // x term
vec![0, 1, 1], // yz term
vec![2, 0, 2], // x^2 z^2 term
vec![2, 1, 0], // x^2 y term
vec![2, 2, 2], // x^2 y^2 z^2 term
];
let coefficients = vec![
F::from(1), // Constant term
F::from(6), // x term
F::from(4), // yz term
F::from(5), // x^2 z^2 term
F::from(2), // x^2 y term
F::from(3), // x^2 y^2 z^2 term
];
MultiVarPolynomial::from_coordinates(coordinates, coefficients).unwrap()
}

fn main() {
println!("Sum-Check Protocol Demonstration");
println!("================================");

let poly = create_demo_polynomial();
println!("Created multivariate polynomial:");
println!("3 x^2 y^2 z^2 + 2x^2 y + 5x^2 z^2 + 4yz + 6x + 1");

let expected_sum = F::from(57);
println!("\nExpected sum over boolean hypercube: {:?}", expected_sum);

let mut sumcheck = SumCheck::new(poly, true);
println!("\nRunning interactive sum-check protocol:");
sumcheck.run_interactive_protocol();

println!("\nVerification result:");
if sumcheck.verifier.result == expected_sum {
println!("Sum-check protocol succeeded! The sum is verified to be {:?}", expected_sum);
} else {
println!(
"Sum-check protocol failed. Expected {:?}, but got {:?}",
expected_sum, sumcheck.verifier.result
);
}
}
2 changes: 2 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,9 @@ pub mod ecdsa;
pub mod encryption;
pub mod hashes;
pub mod kzg;
pub mod multi_var_poly;
pub mod polynomial;
pub mod sumcheck;
pub mod tree;

use core::{
Expand Down
107 changes: 107 additions & 0 deletions src/multi_var_poly/arithmetic.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
//! Arithmetic operations for multivariate polynomials.
//! The operations are implemented for [`MultiVarPolynomial`] in the monomial basis.
//!
//! Note: Operations are restricted to polynomials with the same degree structure.
//!
//! ## Implementations
//! - [`Add`] for adding two multivariate polynomials.
//! - [`AddAssign`] for adding two multivariate polynomials in place.
//! - [`Sum`] for summing a collection of multivariate polynomials.
//! - [`Sub`] for subtracting two multivariate polynomials.
//! - [`SubAssign`] for subtracting two multivariate polynomials in place.
//! - [`Neg`] for negating a multivariate polynomial.
//! - [`Mul`] for scalar multiplication of a multivariate polynomial.
//! - [`MulAssign`] for scalar multiplication of a multivariate polynomial in place.

use std::{
iter::Sum,
ops::{Add, AddAssign, Neg, Sub, SubAssign},
};

use super::*;

impl<F: FiniteField> Add for MultiVarPolynomial<F> {
type Output = Self;

/// Implements addition of two multivariate polynomials by adding their coefficients.
fn add(self, rhs: Self) -> Self::Output {
assert_eq!(self.degree, rhs.degree, "Polynomials must have the same degree structure");

let coefficients =
self.coefficients.iter().zip(rhs.coefficients.iter()).map(|(&a, &b)| a + b).collect();

Self { degree: self.degree, coefficients }
}
}

impl<F: FiniteField> AddAssign for MultiVarPolynomial<F> {
/// Implements in-place addition of two multivariate polynomials by adding their coefficients.
fn add_assign(&mut self, rhs: Self) {
assert_eq!(self.degree, rhs.degree, "Polynomials must have the same degree structure");

for (a, b) in self.coefficients.iter_mut().zip(rhs.coefficients.iter()) {
*a += *b;
}
}
}

impl<F: FiniteField> Sum for MultiVarPolynomial<F> {
/// Implements summing a collection of multivariate polynomials.
fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
iter.reduce(|x, y| x + y).expect("Cannot sum an empty iterator of MultiVarPolynomials")
}
}

impl<F: FiniteField> Sub for MultiVarPolynomial<F> {
type Output = Self;

/// Implements subtraction of two multivariate polynomials by subtracting their coefficients.
fn sub(self, rhs: Self) -> Self::Output {
assert_eq!(self.degree, rhs.degree, "Polynomials must have the same degree structure");

let coefficients =
self.coefficients.iter().zip(rhs.coefficients.iter()).map(|(&a, &b)| a - b).collect();

Self { degree: self.degree, coefficients }
}
}

impl<F: FiniteField> SubAssign for MultiVarPolynomial<F> {
/// Implements in-place subtraction of two multivariate polynomials by subtracting their
/// coefficients.
fn sub_assign(&mut self, rhs: Self) {
assert_eq!(self.degree, rhs.degree, "Polynomials must have the same degree structure");

for (a, b) in self.coefficients.iter_mut().zip(rhs.coefficients.iter()) {
*a -= *b;
}
}
}

impl<F: FiniteField> Neg for MultiVarPolynomial<F> {
type Output = Self;

/// Implements negation of a multivariate polynomial by negating its coefficients.
fn neg(self) -> Self::Output {
Self {
degree: self.degree,
coefficients: self.coefficients.into_iter().map(|c| -c).collect(),
}
}
}

impl<F: FiniteField> Mul<F> for MultiVarPolynomial<F> {
type Output = Self;

/// Implements scalar multiplication of a multivariate polynomial.
fn mul(self, rhs: F) -> Self::Output { self.scalar_mul(rhs) }
}

impl<F: FiniteField> MulAssign<F> for MultiVarPolynomial<F> {
/// Implements in-place scalar multiplication of a multivariate polynomial.
fn mul_assign(&mut self, rhs: F) {
for coeff in &mut self.coefficients {
*coeff *= rhs;
}
}
}
Loading