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

Support scalar mul of arkworks curve using precomputed table #34

Merged
merged 8 commits into from
Mar 26, 2022
Merged
Show file tree
Hide file tree
Changes from 6 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
10 changes: 6 additions & 4 deletions manta-crypto/src/ecc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,9 @@ pub trait ScalarMul<COM = ()> {
/// Elliptic Curve Pre-processed Scalar Multiplication Operation
pub trait PreprocessedScalarMul<COM, const N: usize>: ScalarMul<COM> + Sized {
tsunrise marked this conversation as resolved.
Show resolved Hide resolved
/// Performs the scalar multiplication against a pre-computed table.
///
/// The pre-computed table is powers of two of `scalar`, such that
/// `table[i] = 2^i * G`.
#[must_use]
fn preprocessed_scalar_mul(
table: &[Self; N],
Expand All @@ -162,7 +165,7 @@ where
/// Elliptic Curve Group
pub trait Group<COM = ()>: PointAdd<COM> + PointDouble<COM> + ScalarMul<COM> {}

/// Pre-processed Scalar Multiplication Table
/// Pre-processed Scalar Multiplication Table, represented as powers of two of `scalar`.
tsunrise marked this conversation as resolved.
Show resolved Hide resolved
#[cfg_attr(
feature = "serde",
derive(Deserialize, Serialize),
Expand All @@ -180,17 +183,16 @@ pub struct PreprocessedScalarMulTable<G, const N: usize> {
}

impl<G, const N: usize> PreprocessedScalarMulTable<G, N> {
/// Builds a new [`PreprocessedScalarMulTable`] collection from `base`.
/// Builds a new [`PreprocessedScalarMulTable`] collection from `base`, such that `table[i] = 2^i * base`.
#[inline]
pub fn from_base<COM>(mut base: G, compiler: &mut COM) -> Self
where
G: Clone + PointAdd<COM, Output = G> + PointDouble<COM, Output = G>,
{
let mut powers = Vec::with_capacity(N);
let double = base.double(compiler);
for _ in 0..N {
powers.push(base.clone());
base.add_assign(&double, compiler);
base.double_assign(compiler);
}
Self::from_powers_unchecked(
powers
Expand Down
2 changes: 1 addition & 1 deletion manta-pay/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ simulation = [
std = ["manta-accounting/std", "manta-util/std"]

# Testing Frameworks
test = ["manta-accounting/test", "manta-crypto/test"]
test = ["manta-accounting/test", "manta-crypto/test", "manta-crypto/getrandom"]
tsunrise marked this conversation as resolved.
Show resolved Hide resolved

# Wallet
wallet = ["bip32", "manta-crypto/getrandom", "std"]
Expand Down
135 changes: 135 additions & 0 deletions manta-pay/src/crypto/ecc/arkworks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ use manta_util::serde::{Deserialize, Serialize, Serializer};

pub use ark_ec::{AffineCurve, ProjectiveCurve};
pub use ark_r1cs_std::groups::CurveVar;
use manta_crypto::ecc::{PointAdd, PointDouble};
tsunrise marked this conversation as resolved.
Show resolved Hide resolved

/// Constraint Field Type
type ConstraintField<C> = <<C as ProjectiveCurve>::BaseField as Field>::BasePrimeField;
Expand Down Expand Up @@ -363,6 +364,7 @@ where
}

/// Elliptic Curve Group Element Variable
#[derive(Clone)]
tsunrise marked this conversation as resolved.
Show resolved Hide resolved
pub struct GroupVar<C, CV>(pub(crate) CV, PhantomData<C>)
where
C: ProjectiveCurve,
Expand Down Expand Up @@ -406,6 +408,48 @@ where
}
}

macro_rules! impl_processed_scalar_mul {
($curve: ty) => {
impl<CV>
ecc::PreprocessedScalarMul<
Compiler<$curve>,
{
<<$curve as ProjectiveCurve>::ScalarField as PrimeField>::Params::MODULUS_BITS
as usize
},
tsunrise marked this conversation as resolved.
Show resolved Hide resolved
> for GroupVar<$curve, CV>
where
CV: CurveVar<$curve, ConstraintField<$curve>>,
{
#[inline]
fn preprocessed_scalar_mul(
tsunrise marked this conversation as resolved.
Show resolved Hide resolved
table: &[Self; {
<<$curve as ProjectiveCurve>::ScalarField as PrimeField>::Params::MODULUS_BITS
as usize
}],
scalar: &Self::Scalar,
compiler: &mut Compiler<$curve>,
) -> Self::Output {
let _ = compiler;
let mut result = CV::zero();
let scalar_bits = scalar
.0
.to_bits_le()
.expect("Bit decomposition is not allowed to fail.");
// TODO: Add `+` implementations, `conditional_add` to avoid unnecessary clones.
for (bit, base) in scalar_bits.into_iter().zip(table.iter()) {
result = bit
.select(&(result.clone() + &base.0), &result)
.expect("Conditional select is not allowed to fail. ");
}
Self(result, PhantomData)
}
}
};
}

impl_processed_scalar_mul!(ark_ed_on_bls12_381::EdwardsProjective);

impl<C, CV> Equal<Compiler<C>> for GroupVar<C, CV>
where
C: ProjectiveCurve,
Expand Down Expand Up @@ -495,3 +539,94 @@ where
)
}
}

impl<C, CV> PointAdd<Compiler<C>> for GroupVar<C, CV>
where
C: ProjectiveCurve,
CV: CurveVar<C, ConstraintField<C>>,
{
type Output = Self;

fn add(&self, rhs: &Self, compiler: &mut Compiler<C>) -> Self::Output {
tsunrise marked this conversation as resolved.
Show resolved Hide resolved
let _ = compiler;
let mut result = self.0.clone();
tsunrise marked this conversation as resolved.
Show resolved Hide resolved
result += &rhs.0;
Self::new(result)
}
}

impl<C, CV> PointDouble<Compiler<C>> for GroupVar<C, CV>
where
C: ProjectiveCurve,
CV: CurveVar<C, ConstraintField<C>>,
{
type Output = Self;

fn double(&self, compiler: &mut Compiler<C>) -> Self::Output {
tsunrise marked this conversation as resolved.
Show resolved Hide resolved
let _ = compiler;
Self::new(self.0.double().expect("Doubling is not allowed to fail."))
}
}

#[cfg(test)]
mod tests {
tsunrise marked this conversation as resolved.
Show resolved Hide resolved
use super::*;
use ark_ec::ProjectiveCurve;
use ark_ed_on_bls12_381::{constraints::EdwardsVar, EdwardsProjective};
use manta_crypto::{
constraint::ConstraintSystem,
ecc::{PreprocessedScalarMulTable, ScalarMul},
rand::OsRng,
};

fn preprocessed_scalar_mul_test_template<C, CV, const N: usize>(rng: &mut OsRng)
tsunrise marked this conversation as resolved.
Show resolved Hide resolved
where
C: ProjectiveCurve,
CV: CurveVar<C, ConstraintField<C>>,
GroupVar<C, CV>: ecc::PreprocessedScalarMul<
tsunrise marked this conversation as resolved.
Show resolved Hide resolved
Compiler<C>,
N,
Scalar = ScalarVar<C, CV>,
Output = GroupVar<C, CV>,
>,
{
const NUM_TRIALS: usize = 5;

let mut cs = R1CS::for_known();

for _ in 0..NUM_TRIALS {
let base = Group::<C>::gen(rng);
let base_var = <GroupVar<C, CV> as Variable<Secret, _>>::new_known(&base, &mut cs);

let scalar = Scalar::<C>::gen(rng);
let scalar_var = scalar.as_known::<Secret, ScalarVar<C, CV>>(&mut cs);
tsunrise marked this conversation as resolved.
Show resolved Hide resolved

let expected = ScalarMul::scalar_mul(&base_var, &scalar_var, &mut cs);

let table = PreprocessedScalarMulTable::<_, N>::from_base(base_var, &mut cs);
let actual = table.scalar_mul(&scalar_var, &mut cs);

cs.assert_eq(&expected, &actual);
tsunrise marked this conversation as resolved.
Show resolved Hide resolved
}

assert!(cs.cs.is_satisfied().unwrap());
tsunrise marked this conversation as resolved.
Show resolved Hide resolved
}

#[test]
fn preprocessed_scalar_mul_test() {
tsunrise marked this conversation as resolved.
Show resolved Hide resolved
macro_rules! test_on_curve {
($curve: ty, $var: ty, $rng: expr) => {
preprocessed_scalar_mul_test_template::<
$curve,
$var,
{
<<$curve as ProjectiveCurve>::ScalarField as PrimeField>::Params::MODULUS_BITS as usize
},
>($rng)
}
}

let mut rng = OsRng::default();
test_on_curve!(EdwardsProjective, EdwardsVar, &mut rng);
}
}