From b9d6df913324d54ce14e64c3db464fee3e1466e2 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Sat, 30 May 2020 18:35:33 +1200 Subject: [PATCH] pairing: Extract Engine::miller_loop into a MultiMillerLoop trait This enables MultiMillerLoop to be conditionally implemented, for example in libraries where Engine::pairing supports no-std, but MultiMillerLoop requires an allocator. --- bellman/src/groth16/tests/dummy_engine.rs | 29 ++++++++++-------- bellman/src/groth16/verifier.rs | 17 +++++------ pairing/benches/bls12_381/mod.rs | 12 ++++---- pairing/src/bls12_381/mod.rs | 36 +++++++++++++---------- pairing/src/lib.rs | 32 ++++++++++---------- pairing/src/tests/engine.rs | 35 +++++++++++----------- 6 files changed, 84 insertions(+), 77 deletions(-) diff --git a/bellman/src/groth16/tests/dummy_engine.rs b/bellman/src/groth16/tests/dummy_engine.rs index 465bc2d70..2a209b1a3 100644 --- a/bellman/src/groth16/tests/dummy_engine.rs +++ b/bellman/src/groth16/tests/dummy_engine.rs @@ -1,6 +1,6 @@ use ff::{Field, PrimeField, ScalarEngine}; use group::{CurveAffine, CurveProjective, Group, PrimeGroup}; -use pairing::{Engine, MillerLoopResult, PairingCurveAffine}; +use pairing::{Engine, MillerLoopResult, MultiMillerLoop, PairingCurveAffine}; use rand_core::RngCore; use std::fmt; @@ -335,21 +335,26 @@ impl Engine for DummyEngine { type G2Affine = Fr; // TODO: This should be F_645131 or something. Doesn't matter for now. - type MillerLoopResult = Fr; type Gt = Fr; - fn miller_loop<'a, I>(i: I) -> Self::MillerLoopResult - where - I: IntoIterator< - Item = &'a ( - &'a ::Prepared, - &'a ::Prepared, - ), - >, - { + fn pairing(p: &Self::G1Affine, q: &Self::G2Affine) -> Self::Gt { + Self::multi_miller_loop(&[(p, &(q.prepare()))]).final_exponentiation() + } +} + +impl MultiMillerLoop for DummyEngine { + // TODO: This should be F_645131 or something. Doesn't matter for now. + type Result = Fr; + + fn multi_miller_loop( + terms: &[( + &Self::G1Affine, + &::Prepared, + )], + ) -> Self::Result { let mut acc = ::zero(); - for &(a, b) in i { + for &(a, b) in terms { let mut tmp = *a; MulAssign::mul_assign(&mut tmp, b); AddAssign::add_assign(&mut acc, &tmp); diff --git a/bellman/src/groth16/verifier.rs b/bellman/src/groth16/verifier.rs index 6f144fce7..0825f4f88 100644 --- a/bellman/src/groth16/verifier.rs +++ b/bellman/src/groth16/verifier.rs @@ -1,5 +1,5 @@ use group::{CurveAffine, CurveProjective}; -use pairing::{Engine, MillerLoopResult, PairingCurveAffine}; +use pairing::{Engine, MillerLoopResult, MultiMillerLoop, PairingCurveAffine}; use std::ops::{AddAssign, Neg}; use super::{PreparedVerifyingKey, Proof, VerifyingKey}; @@ -18,7 +18,7 @@ pub fn prepare_verifying_key(vk: &VerifyingKey) -> PreparedVerifyi } } -pub fn verify_proof<'a, E: Engine>( +pub fn verify_proof<'a, E: MultiMillerLoop>( pvk: &'a PreparedVerifyingKey, proof: &Proof, public_inputs: &[E::Fr], @@ -41,14 +41,11 @@ pub fn verify_proof<'a, E: Engine>( // A * B + inputs * (-gamma) + C * (-delta) = alpha * beta // which allows us to do a single final exponentiation. - Ok(E::miller_loop( - [ - (&proof.a.prepare(), &proof.b.prepare()), - (&acc.to_affine().prepare(), &pvk.neg_gamma_g2), - (&proof.c.prepare(), &pvk.neg_delta_g2), - ] - .iter(), - ) + Ok(E::multi_miller_loop(&[ + (&proof.a, &proof.b.prepare()), + (&acc.to_affine(), &pvk.neg_gamma_g2), + (&proof.c, &pvk.neg_delta_g2), + ]) .final_exponentiation() == pvk.alpha_g1_beta_g2) } diff --git a/pairing/benches/bls12_381/mod.rs b/pairing/benches/bls12_381/mod.rs index ba07f2b20..97326c8cf 100644 --- a/pairing/benches/bls12_381/mod.rs +++ b/pairing/benches/bls12_381/mod.rs @@ -10,7 +10,7 @@ use rand_xorshift::XorShiftRng; use group::Group; use pairing::bls12_381::*; -use pairing::{Engine, MillerLoopResult, PairingCurveAffine}; +use pairing::{Engine, MillerLoopResult, MultiMillerLoop, PairingCurveAffine}; fn bench_pairing_g1_preparation(c: &mut Criterion) { const SAMPLES: usize = 1000; @@ -60,10 +60,10 @@ fn bench_pairing_miller_loop(c: &mut Criterion) { 0xe5, ]); - let v: Vec<(G1Prepared, G2Prepared)> = (0..SAMPLES) + let v: Vec<(G1Affine, G2Prepared)> = (0..SAMPLES) .map(|_| { ( - G1Affine::from(G1::random(&mut rng)).prepare(), + G1Affine::from(G1::random(&mut rng)), G2Affine::from(G2::random(&mut rng)).prepare(), ) }) @@ -72,7 +72,7 @@ fn bench_pairing_miller_loop(c: &mut Criterion) { let mut count = 0; c.bench_function("Miller loop", |b| { b.iter(|| { - let tmp = Bls12::miller_loop(&[(&v[count].0, &v[count].1)]); + let tmp = Bls12::multi_miller_loop(&[(&v[count].0, &v[count].1)]); count = (count + 1) % SAMPLES; tmp }) @@ -90,11 +90,11 @@ fn bench_pairing_final_exponentiation(c: &mut Criterion) { let v: Vec = (0..SAMPLES) .map(|_| { ( - G1Affine::from(G1::random(&mut rng)).prepare(), + G1Affine::from(G1::random(&mut rng)), G2Affine::from(G2::random(&mut rng)).prepare(), ) }) - .map(|(ref p, ref q)| Bls12::miller_loop(&[(p, q)])) + .map(|(ref p, ref q)| Bls12::multi_miller_loop(&[(p, q)])) .collect(); let mut count = 0; diff --git a/pairing/src/bls12_381/mod.rs b/pairing/src/bls12_381/mod.rs index 6dd2cb416..3749bb06b 100644 --- a/pairing/src/bls12_381/mod.rs +++ b/pairing/src/bls12_381/mod.rs @@ -21,7 +21,7 @@ pub use self::fq2::Fq2; pub use self::fq6::Fq6; pub use self::fr::{Fr, FrRepr}; -use super::{Engine, MillerLoopResult, PairingCurveAffine}; +use super::{Engine, MillerLoopResult, MultiMillerLoop, PairingCurveAffine}; use ff::{BitIterator, Field, ScalarEngine}; use group::CurveAffine; @@ -43,21 +43,25 @@ impl Engine for Bls12 { type G1Affine = G1Affine; type G2 = G2; type G2Affine = G2Affine; - type MillerLoopResult = Fq12; type Gt = Fq12; - fn miller_loop<'a, I>(i: I) -> Self::MillerLoopResult - where - I: IntoIterator< - Item = &'a ( - &'a ::Prepared, - &'a ::Prepared, - ), - >, - { + fn pairing(p: &Self::G1Affine, q: &Self::G2Affine) -> Self::Gt { + Self::multi_miller_loop(&[(p, &(q.prepare()))]).final_exponentiation() + } +} + +impl MultiMillerLoop for Bls12 { + type Result = Fq12; + + fn multi_miller_loop( + terms: &[( + &Self::G1Affine, + &::Prepared, + )], + ) -> Self::Result { let mut pairs = vec![]; - for &(p, q) in i { - if !p.is_identity() && !q.is_identity() { + for &(p, q) in terms { + if !bool::from(p.is_identity()) && !q.is_identity() { pairs.push((p, q.coeffs.iter())); } } @@ -87,12 +91,12 @@ impl Engine for Bls12 { } for &mut (p, ref mut coeffs) in &mut pairs { - ell(&mut f, coeffs.next().unwrap(), &p.0); + ell(&mut f, coeffs.next().unwrap(), p); } if i { for &mut (p, ref mut coeffs) in &mut pairs { - ell(&mut f, coeffs.next().unwrap(), &p.0); + ell(&mut f, coeffs.next().unwrap(), p); } } @@ -100,7 +104,7 @@ impl Engine for Bls12 { } for &mut (p, ref mut coeffs) in &mut pairs { - ell(&mut f, coeffs.next().unwrap(), &p.0); + ell(&mut f, coeffs.next().unwrap(), p); } if BLS_X_IS_NEGATIVE { diff --git a/pairing/src/lib.rs b/pairing/src/lib.rs index d2a8e63af..23bcad32e 100644 --- a/pairing/src/lib.rs +++ b/pairing/src/lib.rs @@ -64,27 +64,12 @@ pub trait Engine: ScalarEngine { + Mul + for<'a> Mul<&'a Self::Fr, Output = Self::G2>; - /// The type returned by `Engine::miller_loop`. - type MillerLoopResult: MillerLoopResult; - /// The extension field that hosts the target group of the pairing. type Gt: Field; - /// Perform a miller loop with some number of (G1, G2) pairs. - fn miller_loop<'a, I>(i: I) -> Self::MillerLoopResult - where - I: IntoIterator< - Item = &'a ( - &'a ::Prepared, - &'a ::Prepared, - ), - >; - /// Invoke the pairing function `G1 x G2 -> Gt` without the use of precomputation and /// other optimizations. - fn pairing(p: &Self::G1Affine, q: &Self::G2Affine) -> Self::Gt { - Self::miller_loop([(&(p.prepare()), &(q.prepare()))].iter()).final_exponentiation() - } + fn pairing(p: &Self::G1Affine, q: &Self::G2Affine) -> Self::Gt; } /// Affine representation of an elliptic curve point that can be used @@ -101,6 +86,21 @@ pub trait PairingCurveAffine: CurveAffine { fn pairing_with(&self, other: &Self::Pair) -> Self::PairingResult; } +/// An engine that can compute sums of pairings in an efficient way. +pub trait MultiMillerLoop: Engine { + /// The type returned by `Engine::miller_loop`. + type Result: MillerLoopResult; + + /// Computes $$\sum_{i=1}^n \textbf{ML}(a_i, b_i)$$ given a series of terms + /// $$(a_1, b_1), (a_2, b_2), ..., (a_n, b_n).$$ + fn multi_miller_loop( + terms: &[( + &Self::G1Affine, + &::Prepared, + )], + ) -> Self::Result; +} + /// Represents results of a Miller loop, one of the most expensive portions of the pairing /// function. /// diff --git a/pairing/src/tests/engine.rs b/pairing/src/tests/engine.rs index 9968efb0e..a688fc64e 100644 --- a/pairing/src/tests/engine.rs +++ b/pairing/src/tests/engine.rs @@ -4,9 +4,9 @@ use rand_core::SeedableRng; use rand_xorshift::XorShiftRng; use std::ops::MulAssign; -use crate::{Engine, MillerLoopResult, PairingCurveAffine}; +use crate::{Engine, MillerLoopResult, MultiMillerLoop, PairingCurveAffine}; -pub fn engine_tests() { +pub fn engine_tests() { let mut rng = XorShiftRng::from_seed([ 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0xe5, @@ -21,32 +21,32 @@ pub fn engine_tests() { } for _ in 0..1000 { - let z1 = E::G1Affine::identity().prepare(); + let z1 = E::G1Affine::identity(); let z2 = E::G2Affine::identity().prepare(); - let a = E::G1::random(&mut rng).to_affine().prepare(); + let a = E::G1::random(&mut rng).to_affine(); let b = E::G2::random(&mut rng).to_affine().prepare(); - let c = E::G1::random(&mut rng).to_affine().prepare(); + let c = E::G1::random(&mut rng).to_affine(); let d = E::G2::random(&mut rng).to_affine().prepare(); assert_eq!( E::Gt::one(), - E::miller_loop(&[(&z1, &b)]).final_exponentiation() + E::multi_miller_loop(&[(&z1, &b)]).final_exponentiation() ); assert_eq!( E::Gt::one(), - E::miller_loop(&[(&a, &z2)]).final_exponentiation() + E::multi_miller_loop(&[(&a, &z2)]).final_exponentiation() ); assert_eq!( - E::miller_loop(&[(&z1, &b), (&c, &d)]).final_exponentiation(), - E::miller_loop(&[(&a, &z2), (&c, &d)]).final_exponentiation() + E::multi_miller_loop(&[(&z1, &b), (&c, &d)]).final_exponentiation(), + E::multi_miller_loop(&[(&a, &z2), (&c, &d)]).final_exponentiation() ); assert_eq!( - E::miller_loop(&[(&a, &b), (&z1, &d)]).final_exponentiation(), - E::miller_loop(&[(&a, &b), (&c, &z2)]).final_exponentiation() + E::multi_miller_loop(&[(&a, &b), (&z1, &d)]).final_exponentiation(), + E::multi_miller_loop(&[(&a, &b), (&c, &z2)]).final_exponentiation() ); } @@ -54,7 +54,7 @@ pub fn engine_tests() { random_miller_loop_tests::(); } -fn random_miller_loop_tests() { +fn random_miller_loop_tests() { let mut rng = XorShiftRng::from_seed([ 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0xe5, @@ -67,10 +67,10 @@ fn random_miller_loop_tests() { let p2 = E::pairing(&a, &b); - let a = a.prepare(); + let a = a; let b = b.prepare(); - let p1 = E::miller_loop(&[(&a, &b)]).final_exponentiation(); + let p1 = E::multi_miller_loop(&[(&a, &b)]).final_exponentiation(); assert_eq!(p1, p2); } @@ -88,12 +88,13 @@ fn random_miller_loop_tests() { let mut abcd = ab; abcd.mul_assign(&cd); - let a = a.prepare(); + let a = a; let b = b.prepare(); - let c = c.prepare(); + let c = c; let d = d.prepare(); - let abcd_with_double_loop = E::miller_loop(&[(&a, &b), (&c, &d)]).final_exponentiation(); + let abcd_with_double_loop = + E::multi_miller_loop(&[(&a, &b), (&c, &d)]).final_exponentiation(); assert_eq!(abcd, abcd_with_double_loop); }