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

SOT-155: Nova #17

Merged
merged 18 commits into from
Dec 1, 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
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[workspace]
members = ["plonk", "kzg", "fri"]
members = ["plonk", "kzg", "fri", "nova"]
resolver = "2"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
40 changes: 39 additions & 1 deletion kzg/src/scheme.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,14 @@ use ark_ec::pairing::Pairing;
use ark_ec::short_weierstrass::Affine;
use ark_ec::{AffineRepr, CurveGroup};
use ark_ff::{One, Zero};
use ark_poly::univariate::DensePolynomial;
use ark_poly::{DenseUVPolynomial, Polynomial};
use rand::{Rng, RngCore};

use crate::commitment::KzgCommitment;
use crate::opening::KzgOpening;
use crate::srs::Srs;
use crate::types::{G1Point, Poly};
use crate::types::{G1Point, Poly, ScalarField};

/// Implements the KZG polynomial commitment scheme.
///
Expand Down Expand Up @@ -50,6 +51,21 @@ impl KzgScheme {
KzgCommitment(commitment)
}

/// Commits to a coefficient vector using the KZG scheme.
///
/// # Parameters
///
/// - `coeffs`: The coefficient vector to be committed to.
///
/// # Returns
///
/// The commitment to the polynomial.
pub fn commit_vector(&self, coeffs: &[ScalarField]) -> KzgCommitment {
let new_poly = DensePolynomial::from_coefficients_vec(coeffs.into());
let commitment = self.evaluate_in_s(&new_poly);
KzgCommitment(commitment)
}

/// Commits to a parameter using the KZG scheme.
///
/// # Parameters
Expand Down Expand Up @@ -103,6 +119,28 @@ impl KzgScheme {
KzgOpening(opening, evaluation_at_z)
}

/// Opens a commitment at a specified point.
///
/// # Parameters
///
/// - `coeffs`: The coefficient vector to be opened.
/// - `z`: The point at which the polynomial is opened.
///
/// # Returns
///
/// The opening at the specified point.
pub fn open_vector(&self, coeffs: &[ScalarField], z: impl Into<Fr>) -> KzgOpening {
let z = z.into();
let mut polynomial = DensePolynomial::from_coefficients_vec(coeffs.into());
let evaluation_at_z = polynomial.evaluate(&z);
let first = polynomial.coeffs.first_mut().expect("at least 1");
*first -= evaluation_at_z;
let root = Poly::from_coefficients_slice(&[-z, 1.into()]);
let quotient_poly = &polynomial / &root;
let opening = self.evaluate_in_s(&quotient_poly);
KzgOpening(opening, evaluation_at_z)
}

/// Verifies the correctness of an opening.
///
/// # Parameters
Expand Down
2 changes: 2 additions & 0 deletions kzg/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,6 @@ use ark_poly::univariate::DensePolynomial;

pub type G1Point = <Bls12<ark_bls12_381::Config> as Pairing>::G1Affine;
pub type G2Point = <Bls12<ark_bls12_381::Config> as Pairing>::G2Affine;
pub type ScalarField = <Bls12<ark_bls12_381::Config> as Pairing>::ScalarField;
pub type BaseField = <Bls12<ark_bls12_381::Config> as Pairing>::BaseField;
pub type Poly = DensePolynomial<Fr>;
20 changes: 20 additions & 0 deletions nova/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
[package]
name = "nova"
version = "0.1.0"
edition = "2021"


[[example]]
name = "nova-example"
path = "examples/examples.rs"

[dependencies]
ark-ff = "0.4.2"
ark-ec = "0.4.2"
ark-bls12-381 = "0.4.0"
ark-serialize = "0.4.2"
ark-std = "0.4.0"
rand = "0.8.5"
sha2 = "0.10"
kzg = { path = "../kzg" }
plonk = {path = "../plonk"}
199 changes: 199 additions & 0 deletions nova/examples/examples.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
use ark_ff::{BigInteger, One, PrimeField, Zero};
use kzg::scheme::KzgScheme;
use kzg::srs::Srs;
use kzg::types::{BaseField, ScalarField};
use nova::circuit::{AugmentedCircuit, FCircuit, State};
use nova::ivc::{IVCProof, ZkIVCProof, IVC};
use nova::r1cs::{create_trivial_pair, FInstance, FWitness, R1CS};
use nova::transcript::Transcript;
use nova::utils::{to_f_matrix, to_f_vec};
use sha2::Sha256;

struct TestCircuit {}
impl FCircuit for TestCircuit {
fn run(&self, z_i: &State, w_i: &FWitness) -> State {
let x = w_i.w[0];
let res = x * x * x + x + ScalarField::from(5);
let base_res = BaseField::from_le_bytes_mod_order(&res.into_bigint().to_bytes_le());

State {
state: z_i.state + base_res,
}
}
}
fn main() {
// (x0^3 + x0 + 5) + (x1^3 + x1 + 5) + (x2^3 + x2 + 5) + (x3^3 + x2 + 5) = 130
// x0 = 3, x1 = 4, x2 = 1, x3 = 2

// generate R1CS, witnesses and public input, output.
let (r1cs, witnesses, x) = gen_test_values::<ScalarField>(vec![3, 4, 1, 2]);
let (matrix_a, _, _) = (
r1cs.matrix_a.clone(),
r1cs.matrix_b.clone(),
r1cs.matrix_c.clone(),
);

// Trusted setup
let domain_size = witnesses[0].len() + x[0].len() + 1;
let srs = Srs::new(domain_size);
let scheme = KzgScheme::new(srs);
let x_len = x[0].len();

// Generate witnesses and instances
let w: Vec<FWitness> = witnesses
.iter()
.map(|witness| FWitness::new(witness, matrix_a.len()))
.collect();
let mut u: Vec<FInstance> = w
.iter()
.zip(x)
.map(|(w, x)| w.commit(&scheme, &x))
.collect();

// step i
let mut i = BaseField::zero();

// generate trivial instance-witness pair
let (trivial_witness, trivial_instance) =
create_trivial_pair(x_len, witnesses[0].len(), &scheme);

// generate f_circuit instance
let f_circuit = TestCircuit {};

// generate states
let mut z = vec![
State {
state: BaseField::from(0)
};
5
];
for index in 1..5 {
z[index] = f_circuit.run(&z[index - 1], &w[index - 1]);
}

let mut prover_transcript;
let mut verifier_transcript = Transcript::<Sha256>::default();

// create F'
let augmented_circuit =
AugmentedCircuit::<Sha256, TestCircuit>::new(f_circuit, &trivial_instance, &z[0]);

// generate IVC
let mut ivc = IVC::<Sha256, TestCircuit> {
scheme,
augmented_circuit,
};

// initialize IVC proof, zkIVCProof, folded witness and folded instance
let mut ivc_proof = IVCProof::trivial_ivc_proof(&trivial_instance, &trivial_witness);
let mut zk_ivc_proof = ZkIVCProof::trivial_zk_ivc_proof(&trivial_instance);
let mut folded_witness = trivial_witness.clone();
let mut folded_instance = trivial_instance.clone();

let mut res;
for step in 0..4 {
println!("Step: {:?}", step);
if step == 0 {
res = ivc.augmented_circuit.run(&u[step], None, &w[step], None);
} else {
res = ivc.augmented_circuit.run(
&ivc_proof.u_i,
Some(&ivc_proof.big_u_i.clone()),
&ivc_proof.w_i,
Some(&zk_ivc_proof.com_t.clone().unwrap()),
);
}

if res.is_err() {
println!("{:?}", res);
}
assert!(res.is_ok());

// verifier verify this step
let verify = ivc.verify(&zk_ivc_proof, &mut verifier_transcript);
if verify.is_err() {
println!("{:?}", verify);
}
assert!(verify.is_ok());

// update for next step

if step != 3 {
// do not update if we have done with IVC
ivc.augmented_circuit.next_step();
i += BaseField::one();
assert_eq!(ivc.augmented_circuit.z_i.state, z[step + 1].state);
prover_transcript = Transcript::<Sha256>::default();
verifier_transcript = Transcript::<Sha256>::default();

let hash_x = AugmentedCircuit::<Sha256, TestCircuit>::hash_io(
i,
&z[0],
&z[step + 1],
&folded_instance,
);
// convert u_1_x from BaseField into ScalarField
u[step + 1].x = vec![ScalarField::from_le_bytes_mod_order(
&hash_x.into_bigint().to_bytes_le(),
)];

// generate ivc_proof and zkSNARK proof.
ivc_proof = IVCProof::new(
&u[step + 1],
&w[step + 1],
&folded_instance,
&folded_witness,
);
(folded_witness, folded_instance, zk_ivc_proof) =
ivc.prove(&r1cs, &ivc_proof, &mut prover_transcript);
}
}
}

fn gen_test_values<F: PrimeField>(inputs: Vec<usize>) -> (R1CS<F>, Vec<Vec<F>>, Vec<Vec<F>>) {
// R1CS for: x^3 + x + 5 = y (example from article
// https://vitalik.eth.limo/general/2016/12/10/qap.html )

let a = to_f_matrix::<F>(&[
vec![1, 0, 0, 0, 0, 0],
vec![0, 1, 0, 0, 0, 0],
vec![1, 0, 1, 0, 0, 0],
vec![0, 0, 0, 1, 0, 5],
]);
let b = to_f_matrix::<F>(&[
vec![1, 0, 0, 0, 0, 0],
vec![1, 0, 0, 0, 0, 0],
vec![0, 0, 0, 0, 0, 1],
vec![0, 0, 0, 0, 0, 1],
]);
let c = to_f_matrix::<F>(&[
vec![0, 1, 0, 0, 0, 0],
vec![0, 0, 1, 0, 0, 0],
vec![0, 0, 0, 1, 0, 0],
vec![0, 0, 0, 0, 1, 0],
]);

// generate n witnesses
let mut w: Vec<Vec<F>> = Vec::new();
let mut x: Vec<Vec<F>> = Vec::new();
for input in inputs {
let w_i = to_f_vec::<F>(vec![
input,
input * input, // x^2
input * input * input, // x^2 * x
input * input * input + input, // x^3 + x
]);
w.push(w_i.clone());
let x_i = to_f_vec::<F>(vec![input * input * input + input + 5]); // output: x^3 + x + 5
x.push(x_i.clone());
}

let r1cs = R1CS::<F> {
matrix_a: a,
matrix_b: b,
matrix_c: c,
num_io: 1,
num_vars: 4,
};
(r1cs, w, x)
}
2 changes: 2 additions & 0 deletions nova/nova.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
This is a simple implementation of [NOVA](https://eprint.iacr.org/2021/370.pdf).

Loading
Loading