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

chore: Initial impl for KZG10 #234

Draft
wants to merge 3 commits into
base: master
Choose a base branch
from
Draft
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: 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
2 changes: 1 addition & 1 deletion cryptography/bls12_381/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ repository = { workspace = true }
[dependencies]
rayon = { workspace = true }

blst = { version = "0.3", default-features = false }
blst = { version = "0.3", default-features = false, features = ["no-threads"] }
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would usually be controlled by the threadpool, but since its not been integrated, I just turn off all threads


# __private_bench feature is used to allow us to access the base field
blstrs = { version = "0.7.1", features = ["__private_bench"] }
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);
118 changes: 118 additions & 0 deletions cryptography/kzg_single_open/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
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);
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 {
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
}
Loading