Skip to content

Commit

Permalink
add initial impl
Browse files Browse the repository at this point in the history
  • Loading branch information
kevaundray committed Aug 16, 2024
1 parent 7157789 commit 77285bb
Show file tree
Hide file tree
Showing 4 changed files with 179 additions and 0 deletions.
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ members = [
"eip7594",
"cryptography/bls12_381",
"cryptography/kzg_multi_open",
"cryptography/kzg_single_open",
"cryptography/polynomial",
"cryptography/erasure_codes",
]
Expand All @@ -32,6 +33,7 @@ polynomial = { package = "crate_crypto_internal_eth_kzg_polynomial", version = "
erasure_codes = { package = "crate_crypto_internal_eth_kzg_erasure_codes", version = "0.4.1", path = "cryptography/erasure_codes" }
rust_eth_kzg = { version = "0.4.1", path = "eip7594" }
kzg_multi_open = { package = "crate_crypto_kzg_multi_open_fk20", version = "0.4.1", path = "cryptography/kzg_multi_open" }
kzg_single_open = { package = "crate_crypto_kzg_single_open_fk20", version = "0.4.1", path = "cryptography/kzg_single_open" }
c_eth_kzg = { version = "0.4.1", path = "bindings/c" }
hex = "0.4.3"
rayon = "1.10.0"
Expand Down
30 changes: 30 additions & 0 deletions cryptography/kzg_single_open/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
[package]
name = "crate_crypto_kzg_single_open"
description = "This crate provides an implementation for KZG10 suited for DAS in Ethereum"
version = { workspace = true }
authors = { workspace = true }
edition = { workspace = true }
license = { workspace = true }
rust-version = { workspace = true }
repository = { workspace = true }

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
bls12_381 = { workspace = true }
polynomial = { workspace = true }
hex = { workspace = true }
rayon = { workspace = true }
# We should not be importing this library here
# We need it for the CommitKey
kzg_multi_open = { workspace = true }
pairing = { version = "0.23" }


[dev-dependencies]
criterion = "0.5.1"
rand = "0.8.4"

[[bench]]
name = "benchmark"
harness = false
28 changes: 28 additions & 0 deletions cryptography/kzg_single_open/benches/benchmark.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
use bls12_381::Scalar;
use bls12_381::{ff::Field, group::Group, G1Projective};
use crate_crypto_kzg_single_open::compute_proof;
use criterion::{criterion_group, criterion_main, Criterion};
use kzg_multi_open::commit_key::CommitKey;
use polynomial::domain::Domain;

pub fn bench_single_opening_proof(c: &mut Criterion) {
const NUM_G1_ELEMENTS: usize = 4096;

let polynomial_4096: Vec<_> = (0..4096)
.into_iter()
.map(|i| -Scalar::from(i as u64))
.collect();
let G1s: Vec<_> = (0..NUM_G1_ELEMENTS)
.into_iter()
.map(|i| (G1Projective::generator() * (Scalar::from((i + 123456789) as u64))).into())
.collect();
let ck = CommitKey::new(G1s);
let rand_point = Scalar::random(&mut rand::thread_rng());
let domain = Domain::new(4096);
c.bench_function("compute single proof", |b| {
b.iter(|| compute_proof(&ck, &domain, &polynomial_4096, rand_point))
});
}

criterion_group!(benches, bench_single_opening_proof);
criterion_main!(benches);
119 changes: 119 additions & 0 deletions cryptography/kzg_single_open/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
use bls12_381::{multi_pairings, G1Point, G2Point, G2Prepared, Scalar};
use kzg_multi_open::{commit_key::CommitKey, opening_key::OpeningKey};
use polynomial::{domain::Domain, monomial::PolyCoeff};

pub struct Proof {
pub quotient_commitment: bls12_381::G1Point,
pub claimed_evaluation: Scalar,
}

/// Takes a polynomial in lagrange form in reverse bit order and an input point
/// and computes a proof that the polynomial is correctly evaluated at the input point.
///
// Note: The total time taken is around 40-50ms on a single thread. 2-3ms of this is
// division, fft and poly_eval. The rest of it is committing.
pub fn compute_proof(
commit_key: &CommitKey,
domain: &Domain,
polynomial_lagrange: &[Scalar],
input_point: Scalar,
) -> Proof {
// Bit reverse the polynomial and interpolate it.
//
// The bit-reversal is an artifact of a feature we want to maintain
// when we use FK20.
let mut poly_lagrange = polynomial_lagrange.to_vec();
reverse_bit_order(&mut poly_lagrange);
let polynomial_coeff = domain.ifft_scalars(poly_lagrange);

let quotient_poly = divide_by_linear(&polynomial_coeff, input_point);
let quotient_commitment = commit_key.commit_g1(quotient_poly.as_slice());
let claimed_evaluation = poly_eval(&polynomial_coeff, &input_point);

Proof {
quotient_commitment: quotient_commitment.into(),
claimed_evaluation,
}
}

pub(crate) fn reverse_bits(n: usize, bits: u32) -> usize {
let mut n = n;
let mut r = 0;
for _ in 0..bits {
r = (r << 1) | (n & 1);
n >>= 1;
}
r
}

/// Computes log2 of an integer.
///
/// Panics if the integer is not a power of two
pub(crate) fn log2(x: u32) -> u32 {
assert!(x > 0 && x.is_power_of_two(), "x must be a power of two.");
x.trailing_zeros()
}

// Taken and modified from: https://github.com/filecoin-project/ec-gpu/blob/bdde768d0613ae546524c5612e2ad576a646e036/ec-gpu-gen/src/fft_cpu.rs#L10C8-L10C18
pub fn reverse_bit_order<T>(a: &mut [T]) {
let n = a.len() as u32;
assert!(n.is_power_of_two(), "n must be a power of two");
let log_n = log2(n);

for k in 0..n {
let rk = reverse_bits(k as usize, log_n) as u32;
if k < rk {
a.swap(rk as usize, k as usize);
}
}
}

/// Checks that a polynomial `p` was evaluated at a point `z` and returned the value specified `y`.
/// ie. y = p(z).
pub fn verify(
opening_key: OpeningKey,
input_point: Scalar,
output_point: Scalar,
poly_comm: G1Point,
witness_comm: G1Point,
) -> bool {
// For scalar muls could also do precomputations
let inner_a: G1Point = (poly_comm - (opening_key.g1s[0] * output_point)).into();
let inner_b: G2Point = (opening_key.g2s[1] - (opening_key.g2s[0] * input_point)).into();
let prepared_inner_b = G2Prepared::from(-inner_b);

let g2_gen_affine: G2Point = opening_key.g2s[0].into();
let prepared_g2_gen = G2Prepared::from(g2_gen_affine);

multi_pairings(&[
(&inner_a, &prepared_g2_gen),
(&witness_comm, &prepared_inner_b),
])
}

pub fn poly_eval(poly: &PolyCoeff, value: &Scalar) -> Scalar {
let mut result = Scalar::from(0u64);
for coeff in poly.iter().rev() {
result = result * value + coeff;
}
result
}

/// Division using ruffini's rule
fn divide_by_linear(poly: &[Scalar], z: Scalar) -> Vec<Scalar> {
let mut quotient: Vec<Scalar> = Vec::with_capacity(poly.len());
let mut k = Scalar::from(0u64);

for coeff in poly.iter().rev() {
let t = *coeff + k;
quotient.push(t);
k = z * t;
}

// Pop off the remainder term
quotient.pop();

// Reverse the results as monomial form stores coefficients starting with lowest degree
quotient.reverse();
quotient
}

0 comments on commit 77285bb

Please sign in to comment.