Skip to content

Commit

Permalink
Optimize TE2SW for sequences
Browse files Browse the repository at this point in the history
  • Loading branch information
davxy committed Jul 13, 2024
1 parent 2a6d398 commit fcb283d
Show file tree
Hide file tree
Showing 8 changed files with 161 additions and 119 deletions.
3 changes: 3 additions & 0 deletions src/arkworks/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
//! Features expected to land into Arkworks at some point in the future
/// Elligator 2 hash-to-curve.
pub mod elligator2;
/// Twisted Edwards to Short Weierstrass mapping.
pub mod te_sw_map;
123 changes: 123 additions & 0 deletions src/arkworks/te_sw_map.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
use crate::*;
use ark_ec::{
short_weierstrass::{Affine as WeierstrassAffine, SWCurveConfig},
twisted_edwards::{Affine as EdwardsAffine, MontCurveConfig, TECurveConfig},
CurveConfig,
};
use ark_ff::{Field, One};
use ark_std::borrow::Cow;

// Constants used in mapping TE form to SW form and vice versa
pub trait MapConfig: TECurveConfig + SWCurveConfig + MontCurveConfig {
const MONT_A_OVER_THREE: <Self as CurveConfig>::BaseField;
const MONT_B_INV: <Self as CurveConfig>::BaseField;
}

pub fn map_sw_to_te<C: MapConfig>(point: &WeierstrassAffine<C>) -> Option<EdwardsAffine<C>> {
// First map the point from SW to Montgomery
// (Bx - A/3, By)
let mx = <C as MontCurveConfig>::COEFF_B * point.x - C::MONT_A_OVER_THREE;
let my = <C as MontCurveConfig>::COEFF_B * point.y;

// Then we map the TE point to Montgamory
// (x,y) -> (x/y,(x−1)/(x+1))
let v_denom = my.inverse()?;
let x_p_1 = mx + <<C as CurveConfig>::BaseField as One>::one();
let w_denom = x_p_1.inverse()?;
let v = mx * v_denom;
let w = (mx - <<C as CurveConfig>::BaseField as One>::one()) * w_denom;

Some(EdwardsAffine::new_unchecked(v, w))
}

pub fn map_te_to_sw<C: MapConfig>(point: &EdwardsAffine<C>) -> Option<WeierstrassAffine<C>> {
// Map from TE to Montgomery: (1+y)/(1-y), (1+y)/(x(1-y))
let v_denom = <<C as CurveConfig>::BaseField as One>::one() - point.y;
let w_denom = point.x - point.x * point.y;
let v_denom_inv = v_denom.inverse()?;
let w_denom_inv = w_denom.inverse()?;
let v_w_num = <<C as CurveConfig>::BaseField as One>::one() + point.y;
let v = v_w_num * v_denom_inv;
let w = v_w_num * w_denom_inv;

// Map Montgamory to SW: ((x+A/3)/B,y/B)
let x = C::MONT_B_INV * (v + C::MONT_A_OVER_THREE);
let y = C::MONT_B_INV * w;

Some(WeierstrassAffine::new_unchecked(x, y))
}

pub trait SWMapping<C: ark_ec::short_weierstrass::SWCurveConfig> {
fn from_sw(sw: ark_ec::short_weierstrass::Affine<C>) -> Self;
fn into_sw(&self) -> Cow<ark_ec::short_weierstrass::Affine<C>>;
}

impl<C: ark_ec::short_weierstrass::SWCurveConfig> SWMapping<C>
for ark_ec::short_weierstrass::Affine<C>
{
#[inline(always)]
fn from_sw(sw: ark_ec::short_weierstrass::Affine<C>) -> Self {
sw
}

#[inline(always)]
fn into_sw(&self) -> Cow<ark_ec::short_weierstrass::Affine<C>> {
Cow::Borrowed(self)
}
}

impl<C: MapConfig> SWMapping<C> for ark_ec::twisted_edwards::Affine<C> {
#[inline(always)]
fn from_sw(sw: ark_ec::short_weierstrass::Affine<C>) -> Self {
const ERR_MSG: &str =
"SW to TE is expected to be implemented only for curves supporting the mapping";
map_sw_to_te(&sw).expect(ERR_MSG)
}

#[inline(always)]
fn into_sw(&self) -> Cow<ark_ec::short_weierstrass::Affine<C>> {
const ERR_MSG: &str =
"TE to SW is expected to be implemented only for curves supporting the mapping";
Cow::Owned(map_te_to_sw(&self).expect(ERR_MSG))
}
}

pub(crate) trait SWMappingSeq<C: ark_ec::short_weierstrass::SWCurveConfig> {
#[inline(always)]
fn into_sw_seq(&self) -> Cow<[ark_ec::short_weierstrass::Affine<C>]>;
}

impl<C: SWCurveConfig> SWMappingSeq<C> for [ark_ec::short_weierstrass::Affine<C>]
where
ark_ec::short_weierstrass::Affine<C>: SWMapping<C>,
{
#[inline(always)]
fn into_sw_seq(&self) -> Cow<[ark_ec::short_weierstrass::Affine<C>]> {
Cow::Borrowed(self)
}
}

impl<C: MapConfig> SWMappingSeq<C> for [ark_ec::twisted_edwards::Affine<C>]
where
ark_ec::twisted_edwards::Affine<C>: SWMapping<C>,
{
#[inline(always)]
fn into_sw_seq(&self) -> Cow<[WeierstrassAffine<C>]> {
#[cfg(feature = "parallel")]
use rayon::prelude::*;

const ERR_MSG: &str =
"TE to SW is expected to be implemented only for curves supporting the mapping";
#[cfg(feature = "parallel")]
let pks: Vec<_> = self
.par_iter()
.map(|p| map_te_to_sw(p).expect(ERR_MSG))
.collect();
#[cfg(not(feature = "parallel"))]
let pks: Vec<_> = self
.iter()
.map(|p| map_te_to_sw(p).expect(ERR_MSG))
.collect();
Cow::Owned(pks)
}
}
7 changes: 4 additions & 3 deletions src/codec.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use ark_ec::short_weierstrass::SWCurveConfig;
use arkworks::te_sw_map;

use super::*;

Expand Down Expand Up @@ -53,13 +54,13 @@ impl<S: Suite> Codec<S> for Sec1Codec
where
BaseField<S>: ark_ff::PrimeField,
CurveConfig<S>: SWCurveConfig,
AffinePoint<S>: utils::SWMapping<CurveConfig<S>>,
AffinePoint<S>: te_sw_map::SWMapping<CurveConfig<S>>,
{
const BIG_ENDIAN: bool = true;

fn point_encode(pt: &AffinePoint<S>, buf: &mut Vec<u8>) {
use ark_ff::biginteger::BigInteger;
use utils::SWMapping;
use te_sw_map::SWMapping;

if pt.is_zero() {
buf.push(0x00);
Expand All @@ -78,7 +79,7 @@ where

fn point_decode(buf: &[u8]) -> Result<AffinePoint<S>, Error> {
use ark_ff::biginteger::BigInteger;
use utils::SWMapping;
use te_sw_map::SWMapping;
type SWAffine<C> = ark_ec::short_weierstrass::Affine<C>;

if buf.len() == 1 && buf[0] == 0x00 {
Expand Down
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ pub mod utils;
#[cfg(feature = "ring")]
pub mod ring;

#[allow(unused)]
mod arkworks;

#[cfg(test)]
Expand Down
37 changes: 17 additions & 20 deletions src/ring.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::utils::SWMapping;
use crate::arkworks::te_sw_map::{SWMapping, SWMappingSeq};
use crate::*;
use ark_ec::short_weierstrass::SWCurveConfig;
use pedersen::{PedersenSuite, Proof as PedersenProof};
Expand All @@ -8,9 +8,6 @@ pub mod prelude {
pub use ring_proof;
}

#[cfg(feature = "parallel")]
use rayon::prelude::*;

/// Ring suite.
pub trait RingSuite: PedersenSuite {
/// Pairing type.
Expand Down Expand Up @@ -138,7 +135,7 @@ where
) -> Result<(), Error> {
use pedersen::Verifier as PedersenVerifier;
<Self as PedersenVerifier<S>>::verify(input, output, ad, &sig.pedersen_proof)?;
let key_commitment = sig.pedersen_proof.key_commitment().into_sw();
let key_commitment = *sig.pedersen_proof.key_commitment().into_sw();
if !verifier.verify_ring_proof(sig.ring_proof.clone(), key_commitment) {
return Err(Error::VerificationFailure);
}
Expand All @@ -156,11 +153,13 @@ where
pub piop_params: PiopParams<S>,
}

#[inline(always)]
fn domain_size(ring_size: usize) -> usize {
const RING_DOMAIN_OVERHEAD: usize = 257;
1 << ark_std::log2(ring_size + RING_DOMAIN_OVERHEAD)
}

#[allow(private_bounds)]
impl<S: RingSuite> RingContext<S>
where
BaseField<S>: ark_ff::PrimeField,
Expand Down Expand Up @@ -194,8 +193,8 @@ where

let piop_params = PiopParams::<S>::setup(
ring_proof::Domain::new(domain_size, true),
S::BLINDING_BASE.into_sw(),
S::COMPLEMENT_POINT.into_sw(),
*S::BLINDING_BASE.into_sw(),
*S::COMPLEMENT_POINT.into_sw(),
);

Ok(Self {
Expand All @@ -205,31 +204,30 @@ where
}

/// The max ring size this context is able to manage.
#[inline(always)]
pub fn max_ring_size(&self) -> usize {
self.piop_params.keyset_part_size
}

/// Construct a `ProverKey` instance for the given ring.
///
/// Note: if `pks.len() > self.max_ring_size()` the extra keys in the tail are ignored.
pub fn prover_key(&self, pks: &[AffinePoint<S>]) -> ProverKey<S> {
let pks = &pks[..pks.len().min(self.max_ring_size())];
#[cfg(feature = "parallel")]
let pks: Vec<_> = pks.par_iter().map(|p| p.into_sw()).collect();
#[cfg(not(feature = "parallel"))]
let pks: Vec<_> = pks.iter().map(|p| p.into_sw()).collect();
pub fn prover_key(&self, pks: &[AffinePoint<S>]) -> ProverKey<S>
where
[AffinePoint<S>]: SWMappingSeq<CurveConfig<S>>,
{
let pks = pks[..pks.len().min(self.max_ring_size())].into_sw_seq();
ring_proof::index(&self.pcs_params, &self.piop_params, &pks).0
}

/// Construct a `VerifierKey` instance for the given ring.
///
/// Note: if `pks.len() > self.max_ring_size()` the extra keys in the tail are ignored.
pub fn verifier_key(&self, pks: &[AffinePoint<S>]) -> VerifierKey<S> {
let pks = &pks[..pks.len().min(self.max_ring_size())];
#[cfg(feature = "parallel")]
let pks: Vec<_> = pks.par_iter().map(|p| p.into_sw()).collect();
#[cfg(not(feature = "parallel"))]
let pks: Vec<_> = pks.iter().map(|p| p.into_sw()).collect();
pub fn verifier_key(&self, pks: &[AffinePoint<S>]) -> VerifierKey<S>
where
[AffinePoint<S>]: SWMappingSeq<CurveConfig<S>>,
{
let pks = pks[..pks.len().min(self.max_ring_size())].into_sw_seq();
ring_proof::index(&self.pcs_params, &self.piop_params, &pks).1
}

Expand Down Expand Up @@ -297,7 +295,6 @@ impl<S: RingSuite> ark_serialize::Valid for RingContext<S>
where
BaseField<S>: ark_ff::PrimeField,
CurveConfig<S>: SWCurveConfig + Clone,
AffinePoint<S>: SWMapping<CurveConfig<S>>,
{
fn check(&self) -> Result<(), ark_serialize::SerializationError> {
self.pcs_params.check()
Expand Down
8 changes: 4 additions & 4 deletions src/suites/bandersnatch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
//! with `h2c_suite_ID_string` = `"Bandersnatch_XMD:SHA-512_ELL2_RO_"`
//! and domain separation tag `DST = "ECVRF_" || h2c_suite_ID_string || suite_string`.
use crate::{pedersen::PedersenSuite, utils::ark_next::*, *};
use crate::{arkworks::te_sw_map::*, pedersen::PedersenSuite, *};
use ark_ff::MontFp;

pub mod weierstrass {
Expand Down Expand Up @@ -228,17 +228,17 @@ impl MapConfig for ark_ed_on_bls12_381_bandersnatch::BandersnatchConfig {

#[cfg(test)]
mod tests {
use crate::{testing, utils::ark_next};
use crate::{testing, utils::te_sw_map::*};
use ark_ed_on_bls12_381_bandersnatch::{BandersnatchConfig, SWAffine};

#[test]
fn sw_to_te_roundtrip() {
let org_point = testing::random_val::<SWAffine>(None);

let te_point = ark_next::map_sw_to_te::<BandersnatchConfig>(&org_point).unwrap();
let te_point = map_sw_to_te::<BandersnatchConfig>(&org_point).unwrap();
assert!(te_point.is_on_curve());

let sw_point = ark_next::map_te_to_sw::<BandersnatchConfig>(&te_point).unwrap();
let sw_point = map_te_to_sw::<BandersnatchConfig>(&te_point).unwrap();
assert!(sw_point.is_on_curve());
}
}
Expand Down
10 changes: 6 additions & 4 deletions src/testing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,8 @@ pub fn ring_prove_verify<S: ring::RingSuite>()
where
BaseField<S>: ark_ff::PrimeField,
CurveConfig<S>: ark_ec::short_weierstrass::SWCurveConfig + Clone,
AffinePoint<S>: utils::SWMapping<CurveConfig<S>>,
AffinePoint<S>: utils::te_sw_map::SWMapping<CurveConfig<S>>,
[AffinePoint<S>]: utils::te_sw_map::SWMappingSeq<CurveConfig<S>>,
{
use ring::{Prover, RingContext, Verifier};

Expand Down Expand Up @@ -109,10 +110,11 @@ pub fn check_complement_point<S: ring::RingSuite>()
where
BaseField<S>: ark_ff::PrimeField,
CurveConfig<S>: ark_ec::short_weierstrass::SWCurveConfig + Clone,
AffinePoint<S>: utils::SWMapping<CurveConfig<S>>,
AffinePoint<S>: utils::te_sw_map::SWMapping<CurveConfig<S>>,
[AffinePoint<S>]: utils::te_sw_map::SWMappingSeq<CurveConfig<S>>,
{
use utils::SWMapping;
let pt = S::COMPLEMENT_POINT.into_sw();
use utils::te_sw_map::SWMapping;
let pt = *S::COMPLEMENT_POINT.into_sw();
assert!(pt.is_on_curve());
assert!(!pt.is_in_correct_subgroup_assuming_on_curve());
}
Expand Down
Loading

0 comments on commit fcb283d

Please sign in to comment.