Refactoring of multiexp/wnaf.

This commit is contained in:
Sean Bowe 2017-06-17 17:04:14 -06:00
parent 8f6a5737c0
commit d4903f19fe
7 changed files with 514 additions and 312 deletions

View File

@ -58,7 +58,8 @@ macro_rules! curve_impl {
}
}
impl CurveAffine<$engine, $name> for $name_affine {
impl CurveAffine<$engine> for $name_affine {
type Jacobian = $name;
type Uncompressed = $name_uncompressed;
fn is_valid(&self, e: &$engine) -> bool {
@ -133,6 +134,32 @@ macro_rules! curve_impl {
}
}
impl multiexp::Projective<$engine> for $name {
type WindowTable = wnaf::WindowTable<$engine, $name>;
fn identity(e: &$engine) -> Self {
Self::zero(e)
}
fn add_to_projective(&self, e: &$engine, projective: &mut Self) {
projective.add_assign(e, self);
}
fn exponentiate(&mut self,
e: &$engine,
scalar: <$scalarfield as PrimeField<$engine>>::Repr,
table: &mut Self::WindowTable,
scratch: &mut wnaf::WNAFTable
)
{
*self = self.optimal_exp(e, scalar, table, scratch);
}
fn new_window_table(e: &$engine) -> Self::WindowTable {
wnaf::WindowTable::<$engine, $name>::new(e, $name::zero(e), 2)
}
}
impl Curve<$engine> for $name {
type Affine = $name_affine;
type Prepared = $name_prepared;
@ -147,7 +174,7 @@ macro_rules! curve_impl {
None
}
fn optimal_window_batch(&self, engine: &$engine, scalars: usize) -> WindowTable<$engine, $name, Vec<$name>> {
fn optimal_window_batch(&self, engine: &$engine, scalars: usize) -> wnaf::WindowTable<$engine, $name> {
let mut window = engine.$params_field.batch_windows.0;
for i in &engine.$params_field.batch_windows.1 {
@ -158,10 +185,7 @@ macro_rules! curve_impl {
}
}
let mut table = WindowTable::new();
table.set_base(engine, self, window);
table
wnaf::WindowTable::new(engine, *self, window)
}
fn zero(engine: &$engine) -> Self {

View File

@ -283,6 +283,44 @@ macro_rules! fp_impl {
}
}
fn repr_lt(a: &Self::Repr, b: &Self::Repr) -> bool {
$arith_mod::lt(a, b)
}
fn repr_sub_noborrow(a: &mut Self::Repr, b: &Self::Repr) {
$arith_mod::sub_noborrow(a, b);
}
fn repr_add_nocarry(a: &mut Self::Repr, b: &Self::Repr) {
$arith_mod::add_nocarry(a, b);
}
fn repr_num_bits(a: &Self::Repr) -> usize {
$arith_mod::num_bits(a)
}
fn repr_is_zero(a: &Self::Repr) -> bool {
a.iter().all(|&e| e==0)
}
fn repr_is_odd(a: &Self::Repr) -> bool {
$arith_mod::odd(a)
}
fn repr_least_significant_limb(a: &Self::Repr) -> u64 {
a[0]
}
fn repr_div2(a: &mut Self::Repr) {
$arith_mod::div2(a);
}
fn repr_from_u64(a: u64) -> Self::Repr {
let mut tmp = Self::Repr::default();
tmp[0] = a;
tmp
}
fn into_repr(&self, engine: &$engine) -> Self::Repr {
let mut tmp = *self;
tmp.mul_assign(engine, &engine.$params_field.one);

View File

@ -3,7 +3,6 @@ use std::fmt;
use std::borrow::Borrow;
use super::{
WindowTable,
Engine,
Group,
Curve,
@ -15,7 +14,9 @@ use super::{
SqrtField,
BitIterator,
Convert,
Cow
Cow,
multiexp,
wnaf
};
use serde::ser::{Serialize, Serializer, SerializeTuple};
@ -350,7 +351,9 @@ impl<'a> Deserialize<'a> for G2Uncompressed {
}
}
impl CurveRepresentation<Bls381, G1> for G1Uncompressed {
impl CurveRepresentation<Bls381> for G1Uncompressed {
type Affine = G1Affine;
fn to_affine_unchecked(&self, e: &Bls381) -> Result<G1Affine, ()> {
match self {
&G1Uncompressed::Infinity => {
@ -381,7 +384,9 @@ impl CurveRepresentation<Bls381, G1> for G1Uncompressed {
}
}
impl CurveRepresentation<Bls381, G2> for G2Uncompressed {
impl CurveRepresentation<Bls381> for G2Uncompressed {
type Affine = G2Affine;
fn to_affine_unchecked(&self, e: &Bls381) -> Result<G2Affine, ()> {
match self {
&G2Uncompressed::Infinity => {
@ -1037,7 +1042,8 @@ impl Engine for Bls381 {
crossbeam::scope(|scope| {
for (g, s) in g.chunks_mut(chunk).zip(scalars.as_ref().chunks(chunk)) {
scope.spawn(move || {
let mut table = WindowTable::new();
let mut table = wnaf::WindowTable::new(self, G::zero(self), 2);
let mut scratch = wnaf::WNAFTable::new();
for (g, s) in g.iter_mut().zip(s.iter()) {
let mut s = *s;
@ -1047,16 +1053,16 @@ impl Engine for Bls381 {
},
_ => {}
};
let mut newg = g.to_jacobian(self);
opt_exp(self, &mut newg, s.into_repr(self), &mut table);
*g = newg.to_affine(self);
*g = g.to_jacobian(self)
.optimal_exp(self, s.into_repr(self), &mut table, &mut scratch)
.to_affine(self);
}
});
}
});
}
fn batch_baseexp<G: Curve<Self>, S: AsRef<[Self::Fr]>>(&self, table: &WindowTable<Self, G, Vec<G>>, s: S) -> Vec<G::Affine>
fn batch_baseexp<G: Curve<Self>, S: AsRef<[Self::Fr]>>(&self, table: &wnaf::WindowTable<Self, G>, s: S) -> Vec<G::Affine>
{
use crossbeam;
use num_cpus;
@ -1068,13 +1074,12 @@ impl Engine for Bls381 {
let chunk = (s.len() / num_cpus::get()) + 1;
for (s, b) in s.chunks(chunk).zip(ret.chunks_mut(chunk)) {
let mut table = table.shared();
scope.spawn(move || {
let mut scratch = wnaf::WNAFTable::new();
for (s, b) in s.iter().zip(b.iter_mut()) {
let mut tmp = G::zero(self);
table.exp(self, &mut tmp, s.into_repr(self));
*b = tmp.to_affine(self);
scratch.set_scalar(table, s.into_repr(self));
*b = table.exp(self, &scratch).to_affine(self);
}
});
}
@ -1084,232 +1089,7 @@ impl Engine for Bls381 {
}
fn multiexp<G: Curve<Self>>(&self, g: &[G::Affine], s: &[Fr]) -> Result<G, ()> {
if g.len() != s.len() {
return Err(());
}
use crossbeam;
use num_cpus;
return crossbeam::scope(|scope| {
let mut threads = vec![];
let chunk = (s.len() / num_cpus::get()) + 1;
for (g, s) in g.chunks(chunk).zip(s.chunks(chunk)) {
threads.push(scope.spawn(move || {
multiexp_inner(self, g, s)
}));
}
let mut acc = G::zero(self);
for t in threads {
acc.add_assign(self, &t.join());
}
Ok(acc)
});
fn multiexp_inner<G: Curve<Bls381>>(engine: &Bls381, g: &[G::Affine], s: &[Fr]) -> G
{
// This performs a multi-exponentiation calculation, i.e., multiplies
// each group element by the corresponding scalar and adds all of the
// terms together. We use the Bos-Coster algorithm to do this: sort
// the exponents using a max heap, and rewrite the first two terms
// a x + b y = (a-b) x + b(y+x). Reinsert the first element into the
// heap after performing cheap scalar subtraction, and perform the
// point addition. This continues until the heap is emptied as
// elements are multiplied when a certain efficiency threshold is met
// or discarded when their exponents become zero. The result of all
// the multiplications are accumulated and returned when the heap
// is empty.
assert!(g.len() == s.len());
use std::cmp::Ordering;
use std::collections::BinaryHeap;
struct Exp {
index: usize,
value: <Fr as PrimeField<Bls381>>::Repr
}
impl Exp {
fn bits(&self) -> usize {
fr_arith::num_bits(&self.value)
}
fn justexp(&self, sub: &Exp) -> bool {
use std::cmp::min;
let bbits = sub.bits();
let abits = self.bits();
let limit = min(abits-bbits, 20);
if bbits < (1<<limit) {
true
} else {
false
}
}
fn is_zero(&self) -> bool {
self.value.iter().all(|&e| e == 0)
}
}
impl Ord for Exp {
fn cmp(&self, other: &Exp) -> Ordering {
if fr_arith::lt(&self.value, &other.value) {
Ordering::Less
} else if self.value == other.value {
Ordering::Equal
} else {
Ordering::Greater
}
}
}
impl PartialOrd for Exp {
fn partial_cmp(&self, other: &Exp) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl PartialEq for Exp {
fn eq(&self, other: &Exp) -> bool {
self.value == other.value
}
}
impl Eq for Exp { }
let mut result = G::zero(engine);
let one = Fr::one(engine);
let mut elements = Vec::with_capacity(g.len());
let mut heap = BinaryHeap::with_capacity(g.len());
for (g, s) in g.iter().zip(s.iter()) {
if s.is_zero() || g.is_zero() {
// Skip.
continue;
}
if s == &one {
// Just add.
result.add_assign_mixed(engine, &g);
continue;
}
let index = elements.len();
elements.push(g.to_jacobian(engine));
heap.push(Exp {
index: index,
value: s.into_repr(engine)
});
}
let mut table = WindowTable::new();
while let Some(mut greatest) = heap.pop() {
{
let second_greatest = heap.peek();
if second_greatest.is_none() || greatest.justexp(second_greatest.unwrap()) {
// Either this is the last value or multiplying is considered more efficient than
// rewriting and reinsertion into the heap.
opt_exp(engine, &mut elements[greatest.index], greatest.value, &mut table);
result.add_assign(engine, &elements[greatest.index]);
continue;
} else {
// Rewrite
let second_greatest = second_greatest.unwrap();
fr_arith::sub_noborrow(&mut greatest.value, &second_greatest.value);
let mut tmp = elements[second_greatest.index];
tmp.add_assign(engine, &elements[greatest.index]);
elements[second_greatest.index] = tmp;
}
}
if !greatest.is_zero() {
// Reinsert only nonzero scalars.
heap.push(greatest);
}
}
result
}
}
}
impl<G: Curve<Bls381>, B: Borrow<[G]>> WindowTable<Bls381, G, B> {
fn exp(&mut self, e: &Bls381, into: &mut G, mut c: <Fr as PrimeField<Bls381>>::Repr) {
assert!(self.window > 1);
self.wnaf.truncate(0);
self.wnaf.reserve(Fr::num_bits(e) + 1);
// Convert the scalar `c` into wNAF form.
{
use std::default::Default;
let mut tmp = <Fr as PrimeField<Bls381>>::Repr::default();
while !c.iter().all(|&e| e==0) {
let mut u;
if fr_arith::odd(&c) {
u = (c[0] % (1 << (self.window+1))) as i64;
if u > (1 << self.window) {
u -= 1 << (self.window+1);
}
if u > 0 {
tmp[0] = u as u64;
fr_arith::sub_noborrow(&mut c, &tmp);
} else {
tmp[0] = (-u) as u64;
fr_arith::add_nocarry(&mut c, &tmp);
}
} else {
u = 0;
}
self.wnaf.push(u);
fr_arith::div2(&mut c);
}
}
// Perform wNAF exponentiation.
*into = G::zero(e);
for n in self.wnaf.iter().rev() {
into.double(e);
if *n != 0 {
if *n > 0 {
into.add_assign(e, &self.table.borrow()[(n/2) as usize]);
} else {
into.sub_assign(e, &self.table.borrow()[((-n)/2) as usize]);
}
}
}
}
}
// Performs optimal exponentiation
fn opt_exp<G: Curve<Bls381>>(e: &Bls381, base: &mut G, scalar: <Fr as PrimeField<Bls381>>::Repr, table: &mut WindowTable<Bls381, G, Vec<G>>)
{
let bits = fr_arith::num_bits(&scalar);
match G::optimal_window(e, bits) {
Some(window) => {
table.set_base(e, base, window);
table.exp(e, base, scalar);
},
None => {
base.mul_assign(e, &scalar);
}
super::multiexp::perform_multiexp(self, g, s)
}
}

View File

@ -11,7 +11,7 @@ fn test_vectors<E: Engine, G: Curve<E>>(e: &E, expected: &[u8]) {
for _ in 0..10000 {
{
let acc = acc.to_affine(e);
let exp: <G::Affine as CurveAffine<E, G>>::Uncompressed =
let exp: <G::Affine as CurveAffine<E>>::Uncompressed =
bincode::deserialize_from(&mut expected_reader, bincode::Infinite).unwrap();
assert!(acc == exp.to_affine(e).unwrap());

View File

@ -2,12 +2,13 @@ use rand;
use std::fmt;
use std::borrow::Borrow;
use std::marker::PhantomData;
use serde::{Serialize, Deserialize};
use super::{Cow, Convert};
pub mod bls381;
pub mod multiexp;
pub mod wnaf;
pub trait Engine: Sized + Clone + Send + Sync
{
@ -43,7 +44,7 @@ pub trait Engine: Sized + Clone + Send + Sync
/// Perform multi-exponentiation. g and s must have the same length.
fn multiexp<G: Curve<Self>>(&self, g: &[G::Affine], s: &[Self::Fr]) -> Result<G, ()>;
fn batch_baseexp<G: Curve<Self>, S: AsRef<[Self::Fr]>>(&self, table: &WindowTable<Self, G, Vec<G>>, scalars: S) -> Vec<G::Affine>;
fn batch_baseexp<G: Curve<Self>, S: AsRef<[Self::Fr]>>(&self, table: &wnaf::WindowTable<Self, G>, scalars: S) -> Vec<G::Affine>;
fn batchexp<G: Curve<Self>, S: AsRef<[Self::Fr]>>(&self, g: &mut [G::Affine], scalars: S, coeff: Option<&Self::Fr>);
}
@ -63,9 +64,10 @@ pub trait Curve<E: Engine>: Sized +
Sync +
fmt::Debug +
'static +
Group<E>
Group<E> +
self::multiexp::Projective<E>
{
type Affine: CurveAffine<E, Self>;
type Affine: CurveAffine<E, Jacobian=Self>;
type Prepared: Clone + Send + Sync + 'static;
fn zero(&E) -> Self;
@ -86,25 +88,50 @@ pub trait Curve<E: Engine>: Sized +
fn mul_assign<S: Convert<[u64], E>>(&mut self, &E, other: &S);
fn optimal_window(&E, scalar_bits: usize) -> Option<usize>;
fn optimal_window_batch(&self, &E, scalars: usize) -> WindowTable<E, Self, Vec<Self>>;
fn optimal_window_batch(&self, &E, scalars: usize) -> wnaf::WindowTable<E, Self>;
/// Performs optimal exponentiation of this curve element given the scalar, using
/// wNAF when necessary.
fn optimal_exp(
&self,
e: &E,
scalar: <E::Fr as PrimeField<E>>::Repr,
table: &mut wnaf::WindowTable<E, Self>,
scratch: &mut wnaf::WNAFTable
) -> Self {
let bits = E::Fr::repr_num_bits(&scalar);
match Self::optimal_window(e, bits) {
Some(window) => {
table.set_base(e, *self, window);
scratch.set_scalar(table, scalar);
table.exp(e, scratch)
},
None => {
let mut tmp = *self;
tmp.mul_assign(e, &scalar);
tmp
}
}
}
}
pub trait CurveAffine<E: Engine, G: Curve<E>>: Copy +
Clone +
Sized +
Send +
Sync +
fmt::Debug +
PartialEq +
Eq +
'static
pub trait CurveAffine<E: Engine>: Copy +
Clone +
Sized +
Send +
Sync +
fmt::Debug +
PartialEq +
Eq +
'static
{
type Uncompressed: CurveRepresentation<E, G>;
type Jacobian: Curve<E, Affine=Self>;
type Uncompressed: CurveRepresentation<E, Affine=Self>;
fn to_jacobian(&self, &E) -> G;
fn prepare(self, &E) -> G::Prepared;
fn to_jacobian(&self, &E) -> Self::Jacobian;
fn prepare(self, &E) -> <Self::Jacobian as Curve<E>>::Prepared;
fn is_zero(&self) -> bool;
fn mul<S: Convert<[u64], E>>(&self, &E, other: &S) -> G;
fn mul<S: Convert<[u64], E>>(&self, &E, other: &S) -> Self::Jacobian;
fn negate(&mut self, &E);
/// Returns true iff the point is on the curve and in the correct
@ -117,11 +144,13 @@ pub trait CurveAffine<E: Engine, G: Curve<E>>: Copy +
fn to_uncompressed(&self, &E) -> Self::Uncompressed;
}
pub trait CurveRepresentation<E: Engine, G: Curve<E>>: Serialize + for<'a> Deserialize<'a>
pub trait CurveRepresentation<E: Engine>: Serialize + for<'a> Deserialize<'a>
{
type Affine: CurveAffine<E>;
/// If the point representation is valid (lies on the curve, correct
/// subgroup) this function will return it.
fn to_affine(&self, e: &E) -> Result<G::Affine, ()> {
fn to_affine(&self, e: &E) -> Result<Self::Affine, ()> {
let p = try!(self.to_affine_unchecked(e));
if p.is_valid(e) {
@ -133,7 +162,7 @@ pub trait CurveRepresentation<E: Engine, G: Curve<E>>: Serialize + for<'a> Deser
/// Returns the point under the assumption that it is valid. Undefined
/// behavior if `to_affine` would have rejected the point.
fn to_affine_unchecked(&self, &E) -> Result<G::Affine, ()>;
fn to_affine_unchecked(&self, &E) -> Result<Self::Affine, ()>;
}
pub trait Field<E: Engine>: Sized +
@ -189,6 +218,33 @@ pub trait PrimeField<E: Engine>: SqrtField<E> + Convert<[u64], E>
fn from_repr(&E, Self::Repr) -> Result<Self, ()>;
fn into_repr(&self, &E) -> Self::Repr;
/// Determines if a is less than b
fn repr_lt(a: &Self::Repr, b: &Self::Repr) -> bool;
/// Subtracts b from a. Undefined behavior if b > a.
fn repr_sub_noborrow(a: &mut Self::Repr, b: &Self::Repr);
/// Adds b to a. Undefined behavior if overflow occurs.
fn repr_add_nocarry(a: &mut Self::Repr, b: &Self::Repr);
/// Calculates the number of bits.
fn repr_num_bits(a: &Self::Repr) -> usize;
/// Determines if the representation is of a zero.
fn repr_is_zero(a: &Self::Repr) -> bool;
/// Determines if the representation is odd.
fn repr_is_odd(a: &Self::Repr) -> bool;
/// Divides by two via rightshift.
fn repr_div2(a: &mut Self::Repr);
/// Returns the limb of least significance
fn repr_least_significant_limb(a: &Self::Repr) -> u64;
/// Creates a repr given a u64.
fn repr_from_u64(a: u64) -> Self::Repr;
/// Returns an interator over all bits, most significant bit first.
fn bits(&self, &E) -> BitIterator<Self::Repr>;
@ -211,50 +267,6 @@ pub trait SnarkField<E: Engine>: PrimeField<E> + Group<E>
fn root_of_unity(&E) -> Self;
}
pub struct WindowTable<E, G, Table: Borrow<[G]>> {
table: Table,
wnaf: Vec<i64>,
window: usize,
_marker: PhantomData<(E, G)>
}
impl<E: Engine, G: Curve<E>> WindowTable<E, G, Vec<G>> {
fn new() -> Self {
WindowTable {
table: vec![],
wnaf: vec![],
window: 0,
_marker: PhantomData
}
}
fn set_base(&mut self, e: &E, base: &G, window: usize) {
assert!(window > 1);
self.window = window;
self.table.truncate(0);
self.table.reserve(1 << (window-1));
let mut tmp = *base;
let mut dbl = tmp;
dbl.double(e);
for _ in 0..(1 << (window-1)) {
self.table.push(tmp);
tmp.add_assign(e, &dbl);
}
}
fn shared(&self) -> WindowTable<E, G, &[G]> {
WindowTable {
table: &self.table[..],
wnaf: vec![],
window: self.window,
_marker: PhantomData
}
}
}
pub struct BitIterator<T> {
t: T,
n: usize

238
src/curves/multiexp.rs Normal file
View File

@ -0,0 +1,238 @@
//! This module provides an abstract implementation of the Bos-Coster multi-exponentiation algorithm.
use super::{Engine, Curve, CurveAffine, Field, PrimeField};
use super::wnaf;
use std::cmp::Ordering;
use std::collections::BinaryHeap;
pub trait Projective<E: Engine>: Sized + Copy + Clone + Send {
type WindowTable;
/// Constructs an identity element.
fn identity(e: &E) -> Self;
/// Adds this projective element to another projective element.
fn add_to_projective(&self, e: &E, projective: &mut Self);
/// Exponentiates by a scalar.
fn exponentiate(
&mut self,
e: &E,
scalar: <E::Fr as PrimeField<E>>::Repr,
table: &mut Self::WindowTable,
scratch: &mut wnaf::WNAFTable
);
/// Construct a blank window table
fn new_window_table(e: &E) -> Self::WindowTable;
}
pub trait Chunk<E: Engine>: Send {
type Projective: Projective<E>;
/// Skips the next element from the source.
fn skip(&mut self, e: &E) -> Result<(), ()>;
/// Adds the next element from the source to a projective element
fn add_to_projective(&mut self, e: &E, acc: &mut Self::Projective) -> Result<(), ()>;
/// Turns the next element of the source into a projective element.
fn into_projective(&mut self, e: &E) -> Result<Self::Projective, ()>;
}
/// An `ElementSource` is something that contains a sequence of group elements or
/// group element tuples.
pub trait ElementSource<E: Engine> {
type Chunk: Chunk<E>;
/// Gets the number of elements from the source.
fn num_elements(&self) -> usize;
/// Returns a chunk size and a vector of chunks.
fn chunks(&mut self, chunks: usize) -> (usize, Vec<Self::Chunk>);
}
impl<'a, E: Engine, G: CurveAffine<E>> ElementSource<E> for &'a [G] {
type Chunk = &'a [G];
fn num_elements(&self) -> usize {
self.len()
}
fn chunks(&mut self, chunks: usize) -> (usize, Vec<Self::Chunk>) {
let chunk_size = (self.len() / chunks) + 1;
(chunk_size, (*self).chunks(chunk_size).collect())
}
}
impl<'a, E: Engine, G: CurveAffine<E>> Chunk<E> for &'a [G]
{
type Projective = G::Jacobian;
fn skip(&mut self, _: &E) -> Result<(), ()> {
if self.len() == 0 {
Err(())
} else {
*self = &self[1..];
Ok(())
}
}
/// Adds the next element from the source to a projective element
fn add_to_projective(&mut self, e: &E, acc: &mut Self::Projective) -> Result<(), ()> {
if self.len() == 0 {
Err(())
} else {
acc.add_assign_mixed(e, &self[0]);
*self = &self[1..];
Ok(())
}
}
/// Turns the next element of the accumulator into a projective element.
fn into_projective(&mut self, e: &E) -> Result<Self::Projective, ()> {
if self.len() == 0 {
Err(())
} else {
let ret = Ok(self[0].to_jacobian(e));
*self = &self[1..];
ret
}
}
}
fn justexp<E: Engine>(
largest: &<E::Fr as PrimeField<E>>::Repr,
smallest: &<E::Fr as PrimeField<E>>::Repr
) -> bool
{
use std::cmp::min;
let abits = E::Fr::repr_num_bits(largest);
let bbits = E::Fr::repr_num_bits(smallest);
let limit = min(abits-bbits, 20);
if bbits < (1<<limit) {
true
} else {
false
}
}
pub fn perform_multiexp<E: Engine, Source: ElementSource<E>>(
e: &E,
mut bases: Source,
scalars: &[E::Fr]
) -> Result<<Source::Chunk as Chunk<E>>::Projective, ()>
{
if bases.num_elements() != scalars.len() {
return Err(())
}
use crossbeam;
use num_cpus;
let (chunk_len, bases) = bases.chunks(num_cpus::get());
return crossbeam::scope(|scope| {
let mut threads = vec![];
for (mut chunk, scalars) in bases.into_iter().zip(scalars.chunks(chunk_len)) {
threads.push(scope.spawn(move || {
let mut heap: BinaryHeap<Exp<E>> = BinaryHeap::with_capacity(scalars.len());
let mut elements = Vec::with_capacity(scalars.len());
let mut acc = Projective::<E>::identity(e);
let one = E::Fr::one(e);
for scalar in scalars {
if scalar.is_zero() {
// Skip processing bases when we're multiplying by a zero anyway.
chunk.skip(e)?;
} else if *scalar == one {
// Just perform mixed addition when we're multiplying by one.
chunk.add_to_projective(e, &mut acc)?;
} else {
elements.push(chunk.into_projective(e)?);
heap.push(Exp {
scalar: scalar.into_repr(e),
index: elements.len() - 1
});
}
}
let mut window = <<Source::Chunk as Chunk<E>>::Projective as Projective<E>>::new_window_table(e);
let mut scratch = wnaf::WNAFTable::new();
// Now that the heap is populated...
while let Some(mut greatest) = heap.pop() {
{
let second_greatest = heap.peek();
if second_greatest.is_none() || justexp::<E>(&greatest.scalar, &second_greatest.unwrap().scalar) {
// Either this is the last value or multiplying is considered more efficient than
// rewriting and reinsertion into the heap.
//opt_exp(engine, &mut elements[greatest.index], greatest.scalar, &mut table);
elements[greatest.index].exponentiate(e, greatest.scalar, &mut window, &mut scratch);
elements[greatest.index].add_to_projective(e, &mut acc);
continue;
} else {
// Rewrite
let second_greatest = second_greatest.unwrap();
E::Fr::repr_sub_noborrow(&mut greatest.scalar, &second_greatest.scalar);
let mut tmp = elements[second_greatest.index];
elements[greatest.index].add_to_projective(e, &mut tmp);
elements[second_greatest.index] = tmp;
}
}
if !E::Fr::repr_is_zero(&greatest.scalar) {
// Reinsert only nonzero scalars.
heap.push(greatest);
}
}
Ok(acc)
}));
}
let mut acc = Projective::<E>::identity(e);
for t in threads {
t.join()?.add_to_projective(e, &mut acc);
}
Ok(acc)
})
}
struct Exp<E: Engine> {
scalar: <E::Fr as PrimeField<E>>::Repr,
index: usize
}
impl<E: Engine> Ord for Exp<E> {
fn cmp(&self, other: &Exp<E>) -> Ordering {
if E::Fr::repr_lt(&self.scalar, &other.scalar) {
Ordering::Less
} else if self.scalar == other.scalar {
Ordering::Equal
} else {
Ordering::Greater
}
}
}
impl<E: Engine> PartialOrd for Exp<E> {
fn partial_cmp(&self, other: &Exp<E>) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl<E: Engine> PartialEq for Exp<E> {
fn eq(&self, other: &Exp<E>) -> bool {
self.scalar == other.scalar
}
}
impl<E: Engine> Eq for Exp<E> { }

110
src/curves/wnaf.rs Normal file
View File

@ -0,0 +1,110 @@
use std::marker::PhantomData;
use super::{Engine, Curve, PrimeField};
/// Represents the scratch space for a wNAF form scalar.
pub struct WNAFTable {
window: usize,
wnaf: Vec<i64>
}
impl WNAFTable {
pub fn new() -> WNAFTable {
WNAFTable {
window: 0,
wnaf: vec![]
}
}
/// Convert the scalar into wNAF form.
pub fn set_scalar<E: Engine, G: Curve<E>>(&mut self, table: &WindowTable<E, G>, mut c: <E::Fr as PrimeField<E>>::Repr) {
self.window = table.window;
self.wnaf.truncate(0);
while !E::Fr::repr_is_zero(&c) {
let mut u;
if E::Fr::repr_is_odd(&c) {
u = (E::Fr::repr_least_significant_limb(&c) % (1 << (self.window+1))) as i64;
if u > (1 << self.window) {
u -= 1 << (self.window+1);
}
if u > 0 {
E::Fr::repr_sub_noborrow(&mut c, &E::Fr::repr_from_u64(u as u64));
} else {
E::Fr::repr_add_nocarry(&mut c, &E::Fr::repr_from_u64((-u) as u64));
}
} else {
u = 0;
}
self.wnaf.push(u);
E::Fr::repr_div2(&mut c);
}
}
}
/// Represents a window table for a base curve point.
pub struct WindowTable<E: Engine, G: Curve<E>>{
window: usize,
table: Vec<G>,
_marker: PhantomData<E>
}
impl<E: Engine, G: Curve<E>> WindowTable<E, G> {
/// Construct a new window table for a given base.
pub fn new(e: &E, base: G, window: usize) -> Self {
let mut tmp = WindowTable {
window: 0,
table: vec![],
_marker: PhantomData
};
tmp.set_base(e, base, window);
tmp
}
/// Replace this window table with a new one generated by a different base.
pub fn set_base(&mut self, e: &E, mut base: G, window: usize) {
assert!(window < 23);
assert!(window > 1);
self.window = window;
self.table.truncate(0);
self.table.reserve(1 << (window-1));
let mut dbl = base;
dbl.double(e);
for _ in 0..(1 << (window-1)) {
self.table.push(base);
base.add_assign(e, &dbl);
}
}
pub fn exp(&self, e: &E, wnaf: &WNAFTable) -> G {
assert_eq!(wnaf.window, self.window);
let mut result = G::zero(e);
for n in wnaf.wnaf.iter().rev() {
result.double(e);
if *n != 0 {
if *n > 0 {
result.add_assign(e, &self.table[(n/2) as usize]);
} else {
result.sub_assign(e, &self.table[((-n)/2) as usize]);
}
}
}
result
}
pub fn current_window(&self) -> usize {
self.window
}
}