Implement variable-sized windows into multiexp

Closes https://github.com/serai-dex/serai/issues/17 by using the 
PrimeFieldBits API to do so.

Should greatly speed up small batches, along with batches in the 
hundreds. Saves almost a full second on the cross-group DLEq proof.
This commit is contained in:
Luke Parker
2022-06-30 09:30:24 -04:00
parent 5d115f1e1c
commit 7890827a48
15 changed files with 342 additions and 148 deletions

View File

@@ -35,8 +35,6 @@ macro_rules! dalek_curve {
const GENERATOR: Self::G = $POINT;
const GENERATOR_TABLE: Self::T = &$TABLE;
const LITTLE_ENDIAN: bool = true;
fn random_nonce<R: RngCore + CryptoRng>(secret: Self::F, rng: &mut R) -> Self::F {
let mut seed = vec![0; 32];
rng.fill_bytes(&mut seed);

View File

@@ -29,8 +29,6 @@ macro_rules! kp_curve {
const GENERATOR: Self::G = $lib::ProjectivePoint::GENERATOR;
const GENERATOR_TABLE: Self::G = $lib::ProjectivePoint::GENERATOR;
const LITTLE_ENDIAN: bool = false;
fn random_nonce<R: RngCore + CryptoRng>(secret: Self::F, rng: &mut R) -> Self::F {
let mut seed = vec![0; 32];
rng.fill_bytes(&mut seed);

View File

@@ -4,7 +4,8 @@ use thiserror::Error;
use rand_core::{RngCore, CryptoRng};
use group::{ff::PrimeField, Group, GroupOps, prime::PrimeGroup};
use ff::{PrimeField, PrimeFieldBits};
use group::{Group, GroupOps, prime::PrimeGroup};
#[cfg(any(test, feature = "dalek"))]
mod dalek;
@@ -40,7 +41,7 @@ pub enum CurveError {
pub trait Curve: Clone + Copy + PartialEq + Eq + Debug {
/// Scalar field element type
// This is available via G::Scalar yet `C::G::Scalar` is ambiguous, forcing horrific accesses
type F: PrimeField;
type F: PrimeField + PrimeFieldBits;
/// Group element type
type G: Group<Scalar = Self::F> + GroupOps + PrimeGroup;
/// Precomputed table type
@@ -57,9 +58,6 @@ pub trait Curve: Clone + Copy + PartialEq + Eq + Debug {
/// If there isn't a precomputed table available, the generator itself should be used
const GENERATOR_TABLE: Self::T;
/// If little endian is used for the scalar field's Repr
const LITTLE_ENDIAN: bool;
/// Securely generate a random nonce. H4 from the IETF draft
fn random_nonce<R: RngCore + CryptoRng>(secret: Self::F, rng: &mut R) -> Self::F;

View File

@@ -224,7 +224,7 @@ fn complete_r2<R: RngCore + CryptoRng, C: Curve>(
res
};
let mut batch = BatchVerifier::new(shares.len(), C::LITTLE_ENDIAN);
let mut batch = BatchVerifier::new(shares.len());
for (l, share) in &shares {
if *l == params.i() {
continue;
@@ -254,7 +254,7 @@ fn complete_r2<R: RngCore + CryptoRng, C: Curve>(
// Calculate each user's verification share
let mut verification_shares = HashMap::new();
for i in 1 ..= params.n() {
verification_shares.insert(i, multiexp_vartime(&exponential(i, &stripes), C::LITTLE_ENDIAN));
verification_shares.insert(i, multiexp_vartime(&exponential(i, &stripes)));
}
// Removing this check would enable optimizing the above from t + (n * t) to t + ((n - 1) * t)
debug_assert_eq!(C::GENERATOR_TABLE * secret_share, verification_shares[&params.i()]);

View File

@@ -46,7 +46,7 @@ pub(crate) fn batch_verify<C: Curve, R: RngCore + CryptoRng>(
triplets: &[(u16, C::G, C::F, SchnorrSignature<C>)]
) -> Result<(), u16> {
let mut values = [(C::F::one(), C::GENERATOR); 3];
let mut batch = BatchVerifier::new(triplets.len(), C::LITTLE_ENDIAN);
let mut batch = BatchVerifier::new(triplets.len());
for triple in triplets {
// s = r + ca
// sG == R + cA

View File

@@ -21,7 +21,8 @@ pub fn test_curve<R: RngCore + CryptoRng, C: Curve>(rng: &mut R) {
// TODO: Test the Curve functions themselves
// Test successful multiexp, with enough pairs to trigger its variety of algorithms
// TODO: This should probably be under multiexp
// Multiexp has its own tests, yet only against k256 and Ed25519 (which should be sufficient
// as-is to prove multiexp), and this doesn't hurt
{
let mut pairs = Vec::with_capacity(1000);
let mut sum = C::G::identity();
@@ -30,8 +31,8 @@ pub fn test_curve<R: RngCore + CryptoRng, C: Curve>(rng: &mut R) {
pairs.push((C::F::random(&mut *rng), C::GENERATOR * C::F::random(&mut *rng)));
sum += pairs[pairs.len() - 1].1 * pairs[pairs.len() - 1].0;
}
assert_eq!(multiexp::multiexp(&pairs, C::LITTLE_ENDIAN), sum);
assert_eq!(multiexp::multiexp_vartime(&pairs, C::LITTLE_ENDIAN), sum);
assert_eq!(multiexp::multiexp(&pairs), sum);
assert_eq!(multiexp::multiexp_vartime(&pairs), sum);
}
}