diff --git a/snark/src/lib.rs b/snark/src/lib.rs index 68a0a2b..5642852 100644 --- a/snark/src/lib.rs +++ b/snark/src/lib.rs @@ -18,6 +18,8 @@ pub use self::g2::G2; extern "C" { fn libsnarkwrap_init(); fn libsnarkwrap_pairing(p: *const G1, q: *const G2) -> Gt; + fn libsnarkwrap_getqap(d: *mut libc::uint32_t, omega: *mut Fr); + fn libsnarkwrap_test_compare_tau(i: *const G1, tau: *const Fr, d: libc::uint32_t) -> bool; } lazy_static! { @@ -34,6 +36,22 @@ pub fn initialize() { } } +/// Get the QAP info for the generation routines +pub fn getqap() -> (usize, Fr) { + let mut d = 0; + let mut o = Fr::zero(); + + unsafe { libsnarkwrap_getqap(&mut d, &mut o); } + + (d as usize, o) +} + +/// Check that the lagrange coefficients computed by tau over +/// G1 equal the expected vector. +pub fn compare_tau(v: &[G1], tau: &Fr) -> bool { + unsafe { libsnarkwrap_test_compare_tau(&v[0], tau, v.len() as u32) } +} + pub trait Pairing { fn g1<'a>(&'a self, other: &'a Other) -> &'a G1; fn g2<'a>(&'a self, other: &'a Other) -> &'a G2; diff --git a/snark/src/libsnarkwrap.cpp b/snark/src/libsnarkwrap.cpp index 761d640..f90cfa4 100644 --- a/snark/src/libsnarkwrap.cpp +++ b/snark/src/libsnarkwrap.cpp @@ -6,6 +6,7 @@ #include "algebra/curves/public_params.hpp" #include "relations/arithmetic_programs/qap/qap.hpp" #include "reductions/r1cs_to_qap/r1cs_to_qap.hpp" +#include "relations/constraint_satisfaction_problems/r1cs/examples/r1cs_examples.hpp" using namespace std; using namespace libsnark; @@ -168,3 +169,57 @@ extern "C" curve_GT libsnarkwrap_gt_exp(const curve_GT *p, const curve_Fr *s) { extern "C" curve_GT libsnarkwrap_pairing(const curve_G1 *p, const curve_G2 *q) { return curve_pp::reduced_pairing(*p, *q); } + +// QAP + +qap_instance get_qap( + std::shared_ptr> &domain +) +{ + // Generate a dummy circuit + auto example = generate_r1cs_example_with_field_input(250, 4); + + // A/B swap + example.constraint_system.swap_AB_if_beneficial(); + + // QAP reduction + auto qap = r1cs_to_qap_instance_map(example.constraint_system); + + // Degree of the QAP must be a power of 2 + assert(qap.degree() == 256); + + // Assume radix2 evaluation domain + domain = std::static_pointer_cast>(qap.domain); + + return qap; +} + +extern "C" void libsnarkwrap_getqap(uint32_t *d, curve_Fr *omega) +{ + std::shared_ptr> domain; + auto qap = get_qap(domain); + + *omega = domain->omega; + *d = qap.degree(); +} + +extern "C" bool libsnarkwrap_test_compare_tau( + const curve_G1 *inputs, + const curve_Fr *tau, + uint32_t d +) +{ + std::shared_ptr> domain; + auto qap = get_qap(domain); + + auto coeffs = domain->lagrange_coeffs(*tau); + assert(coeffs.size() == d); + assert(qap.degree() == d); + + bool res = true; + for (size_t i = 0; i < d; i++) { + res &= (coeffs[i] * curve_G1::one()) == inputs[i]; + } + + return res; +} diff --git a/src/fft.rs b/src/fft.rs new file mode 100644 index 0000000..24e58a5 --- /dev/null +++ b/src/fft.rs @@ -0,0 +1,70 @@ +use snark::{Group, Fr}; + +pub fn fft(v: &[G], omega: Fr) -> Vec +{ + if v.len() == 2 { + vec![ + v[0] + v[1] * omega, + v[0] + v[1] + ] + } else { + let d2 = v.len() / 2; + let mut evens = Vec::with_capacity(d2); + let mut odds = Vec::with_capacity(d2); + + for (i, x) in v.iter().enumerate() { + if i % 2 == 0 { + evens.push(*x); + } else { + odds.push(*x); + } + } + + let o2 = omega * omega; + let evens = fft(&evens, o2); + let odds = fft(&odds, o2); + + let mut acc = omega; + let mut res = Vec::with_capacity(v.len()); + for i in 0..v.len() { + res.push(evens[i%d2] + odds[i%d2] * acc); + acc = acc * omega; + } + + res + } +} + +#[cfg(test)] +mod test { + use super::fft; + use snark::*; + use util::*; + + #[test] + fn compare_to_libsnark() { + initialize(); + + // Get the QAP degree and omega (for FFT evaluation) + let (d, omega) = getqap(); + + // Sample a random tau + let tau = Fr::random(); + + // Generate powers of tau in G1, from 0 to d exclusive of d + let powers_of_tau = TauPowers::new(tau).take(d).map(|e| G1::one() * e).collect::>(); + + let overd = Fr::from_str(&format!("{}", d)).inverse(); + let lc = fft(&powers_of_tau, omega) // omit tau^d + .into_iter() + .rev() // coefficients are in reverse + .map(|e| e * overd) // divide by d + .collect::>(); + + // Compare against libsnark + assert!(compare_tau(&lc, &tau)); + + // Wrong tau + assert!(!compare_tau(&lc, &Fr::random())); + } +} diff --git a/src/main.rs b/src/main.rs index e2e81f9..52c4ba5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,6 +2,7 @@ extern crate snark; mod randompowers; mod util; +mod fft; use snark::*; diff --git a/src/randompowers.rs b/src/randompowers.rs index 5f7f001..5e648f3 100644 --- a/src/randompowers.rs +++ b/src/randompowers.rs @@ -1,5 +1,5 @@ use snark::*; -use util::Sequences; +use util::*; struct Spair { p: G, @@ -80,27 +80,6 @@ where Group1: Pairing check(Sequences::new(i), a) } -struct TauPowers { - acc: Fr, - tau: Fr -} - -impl TauPowers { - fn new(tau: Fr) -> TauPowers { - TauPowers { acc: Fr::one(), tau: tau } - } -} - -impl Iterator for TauPowers { - type Item = Fr; - - fn next(&mut self) -> Option { - let tmp = self.acc; - self.acc = tmp * self.tau; - Some(tmp) - } -} - #[test] fn randompowers_simulation() { initialize(); diff --git a/src/util.rs b/src/util.rs index eab9f2a..fcaaf00 100644 --- a/src/util.rs +++ b/src/util.rs @@ -1,3 +1,5 @@ +use snark::Fr; + pub struct Sequences<'a, T: 'a, I: Iterator> { v: I, last: Option<&'a T> @@ -34,3 +36,26 @@ fn test_sequences() { let expected = vec![(&a[0], &a[1]), (&a[1], &a[2]), (&a[2], &a[3])]; assert_eq!(b, expected); } + + + +pub struct TauPowers { + acc: Fr, + tau: Fr +} + +impl TauPowers { + pub fn new(tau: Fr) -> TauPowers { + TauPowers { acc: Fr::one(), tau: tau } + } +} + +impl Iterator for TauPowers { + type Item = Fr; + + fn next(&mut self) -> Option { + let tmp = self.acc; + self.acc = tmp * self.tau; + Some(tmp) + } +}