mirror of
https://github.com/Qortal/pirate-librustzcash.git
synced 2025-07-31 12:31:22 +00:00
Move Jubjub, Pedersen hash and primitives into zcash_primitives
This commit is contained in:
@@ -8,6 +8,7 @@ authors = [
|
||||
[dependencies]
|
||||
aes = "0.3"
|
||||
blake2b_simd = "0.5"
|
||||
blake2s_simd = "0.5"
|
||||
byteorder = "1"
|
||||
crypto_api_chachapoly = "0.2.1"
|
||||
ff = { path = "../ff" }
|
||||
@@ -18,8 +19,8 @@ pairing = { path = "../pairing" }
|
||||
rand = "0.7"
|
||||
rand_core = "0.5"
|
||||
rand_os = "0.2"
|
||||
sapling-crypto = { path = "../sapling-crypto" }
|
||||
sha2 = "0.8"
|
||||
|
||||
[dev-dependencies]
|
||||
hex-literal = "0.1"
|
||||
rand_xorshift = "0.2"
|
||||
|
25
zcash_primitives/benches/pedersen_hash.rs
Normal file
25
zcash_primitives/benches/pedersen_hash.rs
Normal file
@@ -0,0 +1,25 @@
|
||||
#![feature(test)]
|
||||
|
||||
extern crate rand_core;
|
||||
extern crate rand_os;
|
||||
extern crate test;
|
||||
extern crate pairing;
|
||||
extern crate zcash_primitives;
|
||||
|
||||
use rand_core::RngCore;
|
||||
use rand_os::OsRng;
|
||||
use pairing::bls12_381::Bls12;
|
||||
use zcash_primitives::jubjub::JubjubBls12;
|
||||
use zcash_primitives::pedersen_hash::{pedersen_hash, Personalization};
|
||||
|
||||
#[bench]
|
||||
fn bench_pedersen_hash(b: &mut test::Bencher) {
|
||||
let params = JubjubBls12::new();
|
||||
let rng = &mut OsRng;
|
||||
let bits = (0..510).map(|_| (rng.next_u32() % 2) != 0).collect::<Vec<_>>();
|
||||
let personalization = Personalization::MerkleTree(31);
|
||||
|
||||
b.iter(|| {
|
||||
pedersen_hash::<Bls12, _>(personalization, bits.clone(), ¶ms)
|
||||
});
|
||||
}
|
40
zcash_primitives/src/constants.rs
Normal file
40
zcash_primitives/src/constants.rs
Normal file
@@ -0,0 +1,40 @@
|
||||
/// First 64 bytes of the BLAKE2s input during group hash.
|
||||
/// This is chosen to be some random string that we couldn't have anticipated when we designed
|
||||
/// the algorithm, for rigidity purposes.
|
||||
/// We deliberately use an ASCII hex string of 32 bytes here.
|
||||
pub const GH_FIRST_BLOCK: &'static [u8; 64]
|
||||
= b"096b36a5804bfacef1691e173c366a47ff5ba84a44f26ddd7e8d9f79d5b42df0";
|
||||
|
||||
// BLAKE2s invocation personalizations
|
||||
/// BLAKE2s Personalization for CRH^ivk = BLAKE2s(ak | nk)
|
||||
pub const CRH_IVK_PERSONALIZATION: &'static [u8; 8]
|
||||
= b"Zcashivk";
|
||||
|
||||
/// BLAKE2s Personalization for PRF^nf = BLAKE2s(nk | rho)
|
||||
pub const PRF_NF_PERSONALIZATION: &'static [u8; 8]
|
||||
= b"Zcash_nf";
|
||||
|
||||
// Group hash personalizations
|
||||
/// BLAKE2s Personalization for Pedersen hash generators.
|
||||
pub const PEDERSEN_HASH_GENERATORS_PERSONALIZATION: &'static [u8; 8]
|
||||
= b"Zcash_PH";
|
||||
|
||||
/// BLAKE2s Personalization for the group hash for key diversification
|
||||
pub const KEY_DIVERSIFICATION_PERSONALIZATION: &'static [u8; 8]
|
||||
= b"Zcash_gd";
|
||||
|
||||
/// BLAKE2s Personalization for the spending key base point
|
||||
pub const SPENDING_KEY_GENERATOR_PERSONALIZATION: &'static [u8; 8]
|
||||
= b"Zcash_G_";
|
||||
|
||||
/// BLAKE2s Personalization for the proof generation key base point
|
||||
pub const PROOF_GENERATION_KEY_BASE_GENERATOR_PERSONALIZATION: &'static [u8; 8]
|
||||
= b"Zcash_H_";
|
||||
|
||||
/// BLAKE2s Personalization for the value commitment generator for the value
|
||||
pub const VALUE_COMMITMENT_GENERATOR_PERSONALIZATION: &'static [u8; 8]
|
||||
= b"Zcash_cv";
|
||||
|
||||
/// BLAKE2s Personalization for the nullifier position generator (for computing rho)
|
||||
pub const NULLIFIER_POSITION_IN_TREE_GENERATOR_PERSONALIZATION: &'static [u8; 8]
|
||||
= b"Zcash_J_";
|
48
zcash_primitives/src/group_hash.rs
Normal file
48
zcash_primitives/src/group_hash.rs
Normal file
@@ -0,0 +1,48 @@
|
||||
use jubjub::{
|
||||
JubjubEngine,
|
||||
PrimeOrder,
|
||||
edwards
|
||||
};
|
||||
|
||||
use ff::{
|
||||
PrimeField
|
||||
};
|
||||
|
||||
use blake2s_simd::Params;
|
||||
use constants;
|
||||
|
||||
/// Produces a random point in the Jubjub curve.
|
||||
/// The point is guaranteed to be prime order
|
||||
/// and not the identity.
|
||||
pub fn group_hash<E: JubjubEngine>(
|
||||
tag: &[u8],
|
||||
personalization: &[u8],
|
||||
params: &E::Params
|
||||
) -> Option<edwards::Point<E, PrimeOrder>>
|
||||
{
|
||||
assert_eq!(personalization.len(), 8);
|
||||
|
||||
// Check to see that scalar field is 255 bits
|
||||
assert!(E::Fr::NUM_BITS == 255);
|
||||
|
||||
let h = Params::new()
|
||||
.hash_length(32)
|
||||
.personal(personalization)
|
||||
.to_state()
|
||||
.update(constants::GH_FIRST_BLOCK)
|
||||
.update(tag)
|
||||
.finalize();
|
||||
|
||||
match edwards::Point::<E, _>::read(h.as_ref(), params) {
|
||||
Ok(p) => {
|
||||
let p = p.mul_by_cofactor(params);
|
||||
|
||||
if p != edwards::Point::zero() {
|
||||
Some(p)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
},
|
||||
Err(_) => None
|
||||
}
|
||||
}
|
533
zcash_primitives/src/jubjub/edwards.rs
Normal file
533
zcash_primitives/src/jubjub/edwards.rs
Normal file
@@ -0,0 +1,533 @@
|
||||
use ff::{BitIterator, Field, PrimeField, PrimeFieldRepr, SqrtField};
|
||||
|
||||
use super::{
|
||||
JubjubEngine,
|
||||
JubjubParams,
|
||||
Unknown,
|
||||
PrimeOrder,
|
||||
montgomery
|
||||
};
|
||||
|
||||
use rand_core::RngCore;
|
||||
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use std::io::{
|
||||
self,
|
||||
Write,
|
||||
Read
|
||||
};
|
||||
|
||||
// Represents the affine point (X/Z, Y/Z) via the extended
|
||||
// twisted Edwards coordinates.
|
||||
//
|
||||
// See "Twisted Edwards Curves Revisited"
|
||||
// Huseyin Hisil, Kenneth Koon-Ho Wong, Gary Carter, and Ed Dawson
|
||||
#[derive(Debug)]
|
||||
pub struct Point<E: JubjubEngine, Subgroup> {
|
||||
x: E::Fr,
|
||||
y: E::Fr,
|
||||
t: E::Fr,
|
||||
z: E::Fr,
|
||||
_marker: PhantomData<Subgroup>
|
||||
}
|
||||
|
||||
fn convert_subgroup<E: JubjubEngine, S1, S2>(from: &Point<E, S1>) -> Point<E, S2>
|
||||
{
|
||||
Point {
|
||||
x: from.x,
|
||||
y: from.y,
|
||||
t: from.t,
|
||||
z: from.z,
|
||||
_marker: PhantomData
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: JubjubEngine> From<&Point<E, Unknown>> for Point<E, Unknown>
|
||||
{
|
||||
fn from(p: &Point<E, Unknown>) -> Point<E, Unknown>
|
||||
{
|
||||
p.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: JubjubEngine> From<Point<E, PrimeOrder>> for Point<E, Unknown>
|
||||
{
|
||||
fn from(p: Point<E, PrimeOrder>) -> Point<E, Unknown>
|
||||
{
|
||||
convert_subgroup(&p)
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: JubjubEngine> From<&Point<E, PrimeOrder>> for Point<E, Unknown>
|
||||
{
|
||||
fn from(p: &Point<E, PrimeOrder>) -> Point<E, Unknown>
|
||||
{
|
||||
convert_subgroup(p)
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: JubjubEngine, Subgroup> Clone for Point<E, Subgroup>
|
||||
{
|
||||
fn clone(&self) -> Self {
|
||||
convert_subgroup(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: JubjubEngine, Subgroup> PartialEq for Point<E, Subgroup> {
|
||||
fn eq(&self, other: &Point<E, Subgroup>) -> bool {
|
||||
// p1 = (x1/z1, y1/z1)
|
||||
// p2 = (x2/z2, y2/z2)
|
||||
// Deciding that these two points are equal is a matter of
|
||||
// determining that x1/z1 = x2/z2, or equivalently that
|
||||
// x1*z2 = x2*z1, and similarly for y.
|
||||
|
||||
let mut x1 = self.x;
|
||||
x1.mul_assign(&other.z);
|
||||
|
||||
let mut y1 = self.y;
|
||||
y1.mul_assign(&other.z);
|
||||
|
||||
let mut x2 = other.x;
|
||||
x2.mul_assign(&self.z);
|
||||
|
||||
let mut y2 = other.y;
|
||||
y2.mul_assign(&self.z);
|
||||
|
||||
x1 == x2 && y1 == y2
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: JubjubEngine> Point<E, Unknown> {
|
||||
pub fn read<R: Read>(
|
||||
reader: R,
|
||||
params: &E::Params
|
||||
) -> io::Result<Self>
|
||||
{
|
||||
let mut y_repr = <E::Fr as PrimeField>::Repr::default();
|
||||
y_repr.read_le(reader)?;
|
||||
|
||||
let x_sign = (y_repr.as_ref()[3] >> 63) == 1;
|
||||
y_repr.as_mut()[3] &= 0x7fffffffffffffff;
|
||||
|
||||
match E::Fr::from_repr(y_repr) {
|
||||
Ok(y) => {
|
||||
match Self::get_for_y(y, x_sign, params) {
|
||||
Some(p) => Ok(p),
|
||||
None => {
|
||||
Err(io::Error::new(io::ErrorKind::InvalidInput, "not on curve"))
|
||||
}
|
||||
}
|
||||
},
|
||||
Err(_) => {
|
||||
Err(io::Error::new(io::ErrorKind::InvalidInput, "y is not in field"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_for_y(y: E::Fr, sign: bool, params: &E::Params) -> Option<Self>
|
||||
{
|
||||
// Given a y on the curve, x^2 = (y^2 - 1) / (dy^2 + 1)
|
||||
// This is defined for all valid y-coordinates,
|
||||
// as dy^2 + 1 = 0 has no solution in Fr.
|
||||
|
||||
// tmp1 = y^2
|
||||
let mut tmp1 = y;
|
||||
tmp1.square();
|
||||
|
||||
// tmp2 = (y^2 * d) + 1
|
||||
let mut tmp2 = tmp1;
|
||||
tmp2.mul_assign(params.edwards_d());
|
||||
tmp2.add_assign(&E::Fr::one());
|
||||
|
||||
// tmp1 = y^2 - 1
|
||||
tmp1.sub_assign(&E::Fr::one());
|
||||
|
||||
match tmp2.inverse() {
|
||||
Some(tmp2) => {
|
||||
// tmp1 = (y^2 - 1) / (dy^2 + 1)
|
||||
tmp1.mul_assign(&tmp2);
|
||||
|
||||
match tmp1.sqrt() {
|
||||
Some(mut x) => {
|
||||
if x.into_repr().is_odd() != sign {
|
||||
x.negate();
|
||||
}
|
||||
|
||||
let mut t = x;
|
||||
t.mul_assign(&y);
|
||||
|
||||
Some(Point {
|
||||
x: x,
|
||||
y: y,
|
||||
t: t,
|
||||
z: E::Fr::one(),
|
||||
_marker: PhantomData
|
||||
})
|
||||
},
|
||||
None => None
|
||||
}
|
||||
},
|
||||
None => None
|
||||
}
|
||||
}
|
||||
|
||||
/// This guarantees the point is in the prime order subgroup
|
||||
#[must_use]
|
||||
pub fn mul_by_cofactor(&self, params: &E::Params) -> Point<E, PrimeOrder>
|
||||
{
|
||||
let tmp = self.double(params)
|
||||
.double(params)
|
||||
.double(params);
|
||||
|
||||
convert_subgroup(&tmp)
|
||||
}
|
||||
|
||||
pub fn rand<R: RngCore>(rng: &mut R, params: &E::Params) -> Self
|
||||
{
|
||||
loop {
|
||||
let y = E::Fr::random(rng);
|
||||
let sign = rng.next_u32() % 2 != 0;
|
||||
|
||||
if let Some(p) = Self::get_for_y(y, sign, params) {
|
||||
return p;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: JubjubEngine, Subgroup> Point<E, Subgroup> {
|
||||
pub fn write<W: Write>(
|
||||
&self,
|
||||
writer: W
|
||||
) -> io::Result<()>
|
||||
{
|
||||
let (x, y) = self.into_xy();
|
||||
|
||||
assert_eq!(E::Fr::NUM_BITS, 255);
|
||||
|
||||
let x_repr = x.into_repr();
|
||||
let mut y_repr = y.into_repr();
|
||||
if x_repr.is_odd() {
|
||||
y_repr.as_mut()[3] |= 0x8000000000000000u64;
|
||||
}
|
||||
|
||||
y_repr.write_le(writer)
|
||||
}
|
||||
|
||||
/// Convert from a Montgomery point
|
||||
pub fn from_montgomery(
|
||||
m: &montgomery::Point<E, Subgroup>,
|
||||
params: &E::Params
|
||||
) -> Self
|
||||
{
|
||||
match m.into_xy() {
|
||||
None => {
|
||||
// Map the point at infinity to the neutral element.
|
||||
Point::zero()
|
||||
},
|
||||
Some((x, y)) => {
|
||||
// The map from a Montgomery curve is defined as:
|
||||
// (x, y) -> (u, v) where
|
||||
// u = x / y
|
||||
// v = (x - 1) / (x + 1)
|
||||
//
|
||||
// This map is not defined for y = 0 and x = -1.
|
||||
//
|
||||
// y = 0 is a valid point only for x = 0:
|
||||
// y^2 = x^3 + A.x^2 + x
|
||||
// 0 = x^3 + A.x^2 + x
|
||||
// 0 = x(x^2 + A.x + 1)
|
||||
// We have: x = 0 OR x^2 + A.x + 1 = 0
|
||||
// x^2 + A.x + 1 = 0
|
||||
// (2.x + A)^2 = A^2 - 4 (Complete the square.)
|
||||
// The left hand side is a square, and so if A^2 - 4
|
||||
// is nonsquare, there is no solution. Indeed, A^2 - 4
|
||||
// is nonsquare.
|
||||
//
|
||||
// (0, 0) is a point of order 2, and so we map it to
|
||||
// (0, -1) in the twisted Edwards curve, which is the
|
||||
// only point of order 2 that is not the neutral element.
|
||||
if y.is_zero() {
|
||||
// This must be the point (0, 0) as above.
|
||||
let mut neg1 = E::Fr::one();
|
||||
neg1.negate();
|
||||
|
||||
Point {
|
||||
x: E::Fr::zero(),
|
||||
y: neg1,
|
||||
t: E::Fr::zero(),
|
||||
z: E::Fr::one(),
|
||||
_marker: PhantomData
|
||||
}
|
||||
} else {
|
||||
// Otherwise, as stated above, the mapping is still
|
||||
// not defined at x = -1. However, x = -1 is not
|
||||
// on the curve when A - 2 is nonsquare:
|
||||
// y^2 = x^3 + A.x^2 + x
|
||||
// y^2 = (-1) + A + (-1)
|
||||
// y^2 = A - 2
|
||||
// Indeed, A - 2 is nonsquare.
|
||||
//
|
||||
// We need to map into (projective) extended twisted
|
||||
// Edwards coordinates (X, Y, T, Z) which represents
|
||||
// the point (X/Z, Y/Z) with Z nonzero and T = XY/Z.
|
||||
//
|
||||
// Thus, we compute...
|
||||
//
|
||||
// u = x(x + 1)
|
||||
// v = y(x - 1)
|
||||
// t = x(x - 1)
|
||||
// z = y(x + 1) (Cannot be nonzero, as above.)
|
||||
//
|
||||
// ... which represents the point ( x / y , (x - 1) / (x + 1) )
|
||||
// as required by the mapping and preserves the property of
|
||||
// the auxiliary coordinate t.
|
||||
//
|
||||
// We need to scale the coordinate, so u and t will have
|
||||
// an extra factor s.
|
||||
|
||||
// u = xs
|
||||
let mut u = x;
|
||||
u.mul_assign(params.scale());
|
||||
|
||||
// v = x - 1
|
||||
let mut v = x;
|
||||
v.sub_assign(&E::Fr::one());
|
||||
|
||||
// t = xs(x - 1)
|
||||
let mut t = u;
|
||||
t.mul_assign(&v);
|
||||
|
||||
// z = (x + 1)
|
||||
let mut z = x;
|
||||
z.add_assign(&E::Fr::one());
|
||||
|
||||
// u = xs(x + 1)
|
||||
u.mul_assign(&z);
|
||||
|
||||
// z = y(x + 1)
|
||||
z.mul_assign(&y);
|
||||
|
||||
// v = y(x - 1)
|
||||
v.mul_assign(&y);
|
||||
|
||||
Point {
|
||||
x: u,
|
||||
y: v,
|
||||
t: t,
|
||||
z: z,
|
||||
_marker: PhantomData
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Attempts to cast this as a prime order element, failing if it's
|
||||
/// not in the prime order subgroup.
|
||||
pub fn as_prime_order(&self, params: &E::Params) -> Option<Point<E, PrimeOrder>> {
|
||||
if self.mul(E::Fs::char(), params) == Point::zero() {
|
||||
Some(convert_subgroup(self))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn zero() -> Self {
|
||||
Point {
|
||||
x: E::Fr::zero(),
|
||||
y: E::Fr::one(),
|
||||
t: E::Fr::zero(),
|
||||
z: E::Fr::one(),
|
||||
_marker: PhantomData
|
||||
}
|
||||
}
|
||||
|
||||
pub fn into_xy(&self) -> (E::Fr, E::Fr)
|
||||
{
|
||||
let zinv = self.z.inverse().unwrap();
|
||||
|
||||
let mut x = self.x;
|
||||
x.mul_assign(&zinv);
|
||||
|
||||
let mut y = self.y;
|
||||
y.mul_assign(&zinv);
|
||||
|
||||
(x, y)
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn negate(&self) -> Self {
|
||||
let mut p = self.clone();
|
||||
|
||||
p.x.negate();
|
||||
p.t.negate();
|
||||
|
||||
p
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn double(&self, _: &E::Params) -> Self {
|
||||
// See "Twisted Edwards Curves Revisited"
|
||||
// Huseyin Hisil, Kenneth Koon-Ho Wong, Gary Carter, and Ed Dawson
|
||||
// Section 3.3
|
||||
// http://hyperelliptic.org/EFD/g1p/auto-twisted-extended.html#doubling-dbl-2008-hwcd
|
||||
|
||||
// A = X1^2
|
||||
let mut a = self.x;
|
||||
a.square();
|
||||
|
||||
// B = Y1^2
|
||||
let mut b = self.y;
|
||||
b.square();
|
||||
|
||||
// C = 2*Z1^2
|
||||
let mut c = self.z;
|
||||
c.square();
|
||||
c.double();
|
||||
|
||||
// D = a*A
|
||||
// = -A
|
||||
let mut d = a;
|
||||
d.negate();
|
||||
|
||||
// E = (X1+Y1)^2 - A - B
|
||||
let mut e = self.x;
|
||||
e.add_assign(&self.y);
|
||||
e.square();
|
||||
e.add_assign(&d); // -A = D
|
||||
e.sub_assign(&b);
|
||||
|
||||
// G = D+B
|
||||
let mut g = d;
|
||||
g.add_assign(&b);
|
||||
|
||||
// F = G-C
|
||||
let mut f = g;
|
||||
f.sub_assign(&c);
|
||||
|
||||
// H = D-B
|
||||
let mut h = d;
|
||||
h.sub_assign(&b);
|
||||
|
||||
// X3 = E*F
|
||||
let mut x3 = e;
|
||||
x3.mul_assign(&f);
|
||||
|
||||
// Y3 = G*H
|
||||
let mut y3 = g;
|
||||
y3.mul_assign(&h);
|
||||
|
||||
// T3 = E*H
|
||||
let mut t3 = e;
|
||||
t3.mul_assign(&h);
|
||||
|
||||
// Z3 = F*G
|
||||
let mut z3 = f;
|
||||
z3.mul_assign(&g);
|
||||
|
||||
Point {
|
||||
x: x3,
|
||||
y: y3,
|
||||
t: t3,
|
||||
z: z3,
|
||||
_marker: PhantomData
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn add(&self, other: &Self, params: &E::Params) -> Self
|
||||
{
|
||||
// See "Twisted Edwards Curves Revisited"
|
||||
// Huseyin Hisil, Kenneth Koon-Ho Wong, Gary Carter, and Ed Dawson
|
||||
// 3.1 Unified Addition in E^e
|
||||
|
||||
// A = x1 * x2
|
||||
let mut a = self.x;
|
||||
a.mul_assign(&other.x);
|
||||
|
||||
// B = y1 * y2
|
||||
let mut b = self.y;
|
||||
b.mul_assign(&other.y);
|
||||
|
||||
// C = d * t1 * t2
|
||||
let mut c = params.edwards_d().clone();
|
||||
c.mul_assign(&self.t);
|
||||
c.mul_assign(&other.t);
|
||||
|
||||
// D = z1 * z2
|
||||
let mut d = self.z;
|
||||
d.mul_assign(&other.z);
|
||||
|
||||
// H = B - aA
|
||||
// = B + A
|
||||
let mut h = b;
|
||||
h.add_assign(&a);
|
||||
|
||||
// E = (x1 + y1) * (x2 + y2) - A - B
|
||||
// = (x1 + y1) * (x2 + y2) - H
|
||||
let mut e = self.x;
|
||||
e.add_assign(&self.y);
|
||||
{
|
||||
let mut tmp = other.x;
|
||||
tmp.add_assign(&other.y);
|
||||
e.mul_assign(&tmp);
|
||||
}
|
||||
e.sub_assign(&h);
|
||||
|
||||
// F = D - C
|
||||
let mut f = d;
|
||||
f.sub_assign(&c);
|
||||
|
||||
// G = D + C
|
||||
let mut g = d;
|
||||
g.add_assign(&c);
|
||||
|
||||
// x3 = E * F
|
||||
let mut x3 = e;
|
||||
x3.mul_assign(&f);
|
||||
|
||||
// y3 = G * H
|
||||
let mut y3 = g;
|
||||
y3.mul_assign(&h);
|
||||
|
||||
// t3 = E * H
|
||||
let mut t3 = e;
|
||||
t3.mul_assign(&h);
|
||||
|
||||
// z3 = F * G
|
||||
let mut z3 = f;
|
||||
z3.mul_assign(&g);
|
||||
|
||||
Point {
|
||||
x: x3,
|
||||
y: y3,
|
||||
t: t3,
|
||||
z: z3,
|
||||
_marker: PhantomData
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn mul<S: Into<<E::Fs as PrimeField>::Repr>>(
|
||||
&self,
|
||||
scalar: S,
|
||||
params: &E::Params
|
||||
) -> Self
|
||||
{
|
||||
// Standard double-and-add scalar multiplication
|
||||
|
||||
let mut res = Self::zero();
|
||||
|
||||
for b in BitIterator::new(scalar.into()) {
|
||||
res = res.double(params);
|
||||
|
||||
if b {
|
||||
res = res.add(self, params);
|
||||
}
|
||||
}
|
||||
|
||||
res
|
||||
}
|
||||
}
|
1273
zcash_primitives/src/jubjub/fs.rs
Normal file
1273
zcash_primitives/src/jubjub/fs.rs
Normal file
File diff suppressed because it is too large
Load Diff
433
zcash_primitives/src/jubjub/mod.rs
Normal file
433
zcash_primitives/src/jubjub/mod.rs
Normal file
@@ -0,0 +1,433 @@
|
||||
//! Jubjub is a twisted Edwards curve defined over the BLS12-381 scalar
|
||||
//! field, Fr. It takes the form `-x^2 + y^2 = 1 + dx^2y^2` with
|
||||
//! `d = -(10240/10241)`. It is birationally equivalent to a Montgomery
|
||||
//! curve of the form `y^2 = x^3 + Ax^2 + x` with `A = 40962`. This
|
||||
//! value `A` is the smallest integer choice such that:
|
||||
//!
|
||||
//! * `(A - 2) / 4` is a small integer (`10240`).
|
||||
//! * `A^2 - 4` is quadratic nonresidue.
|
||||
//! * The group order of the curve and its quadratic twist has a large
|
||||
//! prime factor.
|
||||
//!
|
||||
//! Jubjub has `s = 0x0e7db4ea6533afa906673b0101343b00a6682093ccc81082d0970e5ed6f72cb7`
|
||||
//! as the prime subgroup order, with cofactor 8. (The twist has
|
||||
//! cofactor 4.)
|
||||
//!
|
||||
//! It is a complete twisted Edwards curve, so the equivalence with
|
||||
//! the Montgomery curve forms a group isomorphism, allowing points
|
||||
//! to be freely converted between the two forms.
|
||||
|
||||
use ff::{Field, PrimeField, SqrtField};
|
||||
use pairing::Engine;
|
||||
|
||||
use group_hash::group_hash;
|
||||
|
||||
use constants;
|
||||
|
||||
use pairing::bls12_381::{
|
||||
Bls12,
|
||||
Fr
|
||||
};
|
||||
|
||||
/// This is an implementation of the twisted Edwards Jubjub curve.
|
||||
pub mod edwards;
|
||||
|
||||
/// This is an implementation of the birationally equivalent
|
||||
/// Montgomery curve.
|
||||
pub mod montgomery;
|
||||
|
||||
/// This is an implementation of the scalar field for Jubjub.
|
||||
pub mod fs;
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod tests;
|
||||
|
||||
/// Point of unknown order.
|
||||
#[derive(Debug)]
|
||||
pub enum Unknown { }
|
||||
|
||||
/// Point of prime order.
|
||||
#[derive(Debug)]
|
||||
pub enum PrimeOrder { }
|
||||
|
||||
/// Fixed generators of the Jubjub curve of unknown
|
||||
/// exponent.
|
||||
#[derive(Copy, Clone)]
|
||||
pub enum FixedGenerators {
|
||||
/// The prover will demonstrate knowledge of discrete log
|
||||
/// with respect to this base when they are constructing
|
||||
/// a proof, in order to authorize proof construction.
|
||||
ProofGenerationKey = 0,
|
||||
|
||||
/// The note commitment is randomized over this generator.
|
||||
NoteCommitmentRandomness = 1,
|
||||
|
||||
/// The node commitment is randomized again by the position
|
||||
/// in order to supply the nullifier computation with a
|
||||
/// unique input w.r.t. the note being spent, to prevent
|
||||
/// Faerie gold attacks.
|
||||
NullifierPosition = 2,
|
||||
|
||||
/// The value commitment is used to check balance between
|
||||
/// inputs and outputs. The value is placed over this
|
||||
/// generator.
|
||||
ValueCommitmentValue = 3,
|
||||
/// The value commitment is randomized over this generator,
|
||||
/// for privacy.
|
||||
ValueCommitmentRandomness = 4,
|
||||
|
||||
/// The spender proves discrete log with respect to this
|
||||
/// base at spend time.
|
||||
SpendingKeyGenerator = 5,
|
||||
|
||||
Max = 6
|
||||
}
|
||||
|
||||
pub trait ToUniform {
|
||||
fn to_uniform(digest: &[u8]) -> Self;
|
||||
}
|
||||
|
||||
/// This is an extension to the pairing Engine trait which
|
||||
/// offers a scalar field for the embedded curve (Jubjub)
|
||||
/// and some pre-computed parameters.
|
||||
pub trait JubjubEngine: Engine {
|
||||
/// The scalar field of the Jubjub curve
|
||||
type Fs: PrimeField + SqrtField + ToUniform;
|
||||
/// The parameters of Jubjub and the Sapling protocol
|
||||
type Params: JubjubParams<Self>;
|
||||
}
|
||||
|
||||
/// The pre-computed parameters for Jubjub, including curve
|
||||
/// constants and various limits and window tables.
|
||||
pub trait JubjubParams<E: JubjubEngine>: Sized {
|
||||
/// The `d` constant of the twisted Edwards curve.
|
||||
fn edwards_d(&self) -> &E::Fr;
|
||||
/// The `A` constant of the birationally equivalent Montgomery curve.
|
||||
fn montgomery_a(&self) -> &E::Fr;
|
||||
/// The `A` constant, doubled.
|
||||
fn montgomery_2a(&self) -> &E::Fr;
|
||||
/// The scaling factor used for conversion from the Montgomery form.
|
||||
fn scale(&self) -> &E::Fr;
|
||||
/// Returns the generators (for each segment) used in all Pedersen commitments.
|
||||
fn pedersen_hash_generators(&self) -> &[edwards::Point<E, PrimeOrder>];
|
||||
/// Returns the exp table for Pedersen hashes.
|
||||
fn pedersen_hash_exp_table(&self) -> &[Vec<Vec<edwards::Point<E, PrimeOrder>>>];
|
||||
/// Returns the maximum number of chunks per segment of the Pedersen hash.
|
||||
fn pedersen_hash_chunks_per_generator(&self) -> usize;
|
||||
/// Returns the pre-computed window tables [-4, 3, 2, 1, 1, 2, 3, 4] of different
|
||||
/// magnitudes of the Pedersen hash segment generators.
|
||||
fn pedersen_circuit_generators(&self) -> &[Vec<Vec<(E::Fr, E::Fr)>>];
|
||||
|
||||
/// Returns the number of chunks needed to represent a full scalar during fixed-base
|
||||
/// exponentiation.
|
||||
fn fixed_base_chunks_per_generator(&self) -> usize;
|
||||
/// Returns a fixed generator.
|
||||
fn generator(&self, base: FixedGenerators) -> &edwards::Point<E, PrimeOrder>;
|
||||
/// Returns a window table [0, 1, ..., 8] for different magnitudes of some
|
||||
/// fixed generator.
|
||||
fn circuit_generators(&self, FixedGenerators) -> &[Vec<(E::Fr, E::Fr)>];
|
||||
/// Returns the window size for exponentiation of Pedersen hash generators
|
||||
/// outside the circuit
|
||||
fn pedersen_hash_exp_window_size() -> u32;
|
||||
}
|
||||
|
||||
impl JubjubEngine for Bls12 {
|
||||
type Fs = self::fs::Fs;
|
||||
type Params = JubjubBls12;
|
||||
}
|
||||
|
||||
pub struct JubjubBls12 {
|
||||
edwards_d: Fr,
|
||||
montgomery_a: Fr,
|
||||
montgomery_2a: Fr,
|
||||
scale: Fr,
|
||||
|
||||
pedersen_hash_generators: Vec<edwards::Point<Bls12, PrimeOrder>>,
|
||||
pedersen_hash_exp: Vec<Vec<Vec<edwards::Point<Bls12, PrimeOrder>>>>,
|
||||
pedersen_circuit_generators: Vec<Vec<Vec<(Fr, Fr)>>>,
|
||||
|
||||
fixed_base_generators: Vec<edwards::Point<Bls12, PrimeOrder>>,
|
||||
fixed_base_circuit_generators: Vec<Vec<Vec<(Fr, Fr)>>>,
|
||||
}
|
||||
|
||||
impl JubjubParams<Bls12> for JubjubBls12 {
|
||||
fn edwards_d(&self) -> &Fr { &self.edwards_d }
|
||||
fn montgomery_a(&self) -> &Fr { &self.montgomery_a }
|
||||
fn montgomery_2a(&self) -> &Fr { &self.montgomery_2a }
|
||||
fn scale(&self) -> &Fr { &self.scale }
|
||||
fn pedersen_hash_generators(&self) -> &[edwards::Point<Bls12, PrimeOrder>] {
|
||||
&self.pedersen_hash_generators
|
||||
}
|
||||
fn pedersen_hash_exp_table(&self) -> &[Vec<Vec<edwards::Point<Bls12, PrimeOrder>>>] {
|
||||
&self.pedersen_hash_exp
|
||||
}
|
||||
fn pedersen_hash_chunks_per_generator(&self) -> usize {
|
||||
63
|
||||
}
|
||||
fn fixed_base_chunks_per_generator(&self) -> usize {
|
||||
84
|
||||
}
|
||||
fn pedersen_circuit_generators(&self) -> &[Vec<Vec<(Fr, Fr)>>] {
|
||||
&self.pedersen_circuit_generators
|
||||
}
|
||||
fn generator(&self, base: FixedGenerators) -> &edwards::Point<Bls12, PrimeOrder>
|
||||
{
|
||||
&self.fixed_base_generators[base as usize]
|
||||
}
|
||||
fn circuit_generators(&self, base: FixedGenerators) -> &[Vec<(Fr, Fr)>]
|
||||
{
|
||||
&self.fixed_base_circuit_generators[base as usize][..]
|
||||
}
|
||||
fn pedersen_hash_exp_window_size() -> u32 {
|
||||
8
|
||||
}
|
||||
}
|
||||
|
||||
impl JubjubBls12 {
|
||||
pub fn new() -> Self {
|
||||
let montgomery_a = Fr::from_str("40962").unwrap();
|
||||
let mut montgomery_2a = montgomery_a;
|
||||
montgomery_2a.double();
|
||||
|
||||
let mut tmp_params = JubjubBls12 {
|
||||
// d = -(10240/10241)
|
||||
edwards_d: Fr::from_str("19257038036680949359750312669786877991949435402254120286184196891950884077233").unwrap(),
|
||||
// A = 40962
|
||||
montgomery_a: montgomery_a,
|
||||
// 2A = 2.A
|
||||
montgomery_2a: montgomery_2a,
|
||||
// scaling factor = sqrt(4 / (a - d))
|
||||
scale: Fr::from_str("17814886934372412843466061268024708274627479829237077604635722030778476050649").unwrap(),
|
||||
|
||||
// We'll initialize these below
|
||||
pedersen_hash_generators: vec![],
|
||||
pedersen_hash_exp: vec![],
|
||||
pedersen_circuit_generators: vec![],
|
||||
fixed_base_generators: vec![],
|
||||
fixed_base_circuit_generators: vec![],
|
||||
};
|
||||
|
||||
fn find_group_hash<E: JubjubEngine>(
|
||||
m: &[u8],
|
||||
personalization: &[u8; 8],
|
||||
params: &E::Params
|
||||
) -> edwards::Point<E, PrimeOrder>
|
||||
{
|
||||
let mut tag = m.to_vec();
|
||||
let i = tag.len();
|
||||
tag.push(0u8);
|
||||
|
||||
loop {
|
||||
let gh = group_hash(
|
||||
&tag,
|
||||
personalization,
|
||||
params
|
||||
);
|
||||
|
||||
// We don't want to overflow and start reusing generators
|
||||
assert!(tag[i] != u8::max_value());
|
||||
tag[i] += 1;
|
||||
|
||||
if let Some(gh) = gh {
|
||||
break gh;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create the bases for the Pedersen hashes
|
||||
{
|
||||
let mut pedersen_hash_generators = vec![];
|
||||
|
||||
for m in 0..5 {
|
||||
use byteorder::{WriteBytesExt, LittleEndian};
|
||||
|
||||
let mut segment_number = [0u8; 4];
|
||||
(&mut segment_number[0..4]).write_u32::<LittleEndian>(m).unwrap();
|
||||
|
||||
pedersen_hash_generators.push(
|
||||
find_group_hash(
|
||||
&segment_number,
|
||||
constants::PEDERSEN_HASH_GENERATORS_PERSONALIZATION,
|
||||
&tmp_params
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// Check for duplicates, far worse than spec inconsistencies!
|
||||
for (i, p1) in pedersen_hash_generators.iter().enumerate() {
|
||||
if p1 == &edwards::Point::zero() {
|
||||
panic!("Neutral element!");
|
||||
}
|
||||
|
||||
for p2 in pedersen_hash_generators.iter().skip(i+1) {
|
||||
if p1 == p2 {
|
||||
panic!("Duplicate generator!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tmp_params.pedersen_hash_generators = pedersen_hash_generators;
|
||||
}
|
||||
|
||||
// Create the exp table for the Pedersen hash generators
|
||||
{
|
||||
let mut pedersen_hash_exp = vec![];
|
||||
|
||||
for g in &tmp_params.pedersen_hash_generators {
|
||||
let mut g = g.clone();
|
||||
|
||||
let window = JubjubBls12::pedersen_hash_exp_window_size();
|
||||
|
||||
let mut tables = vec![];
|
||||
|
||||
let mut num_bits = 0;
|
||||
while num_bits <= fs::Fs::NUM_BITS {
|
||||
let mut table = Vec::with_capacity(1 << window);
|
||||
|
||||
let mut base = edwards::Point::zero();
|
||||
|
||||
for _ in 0..(1 << window) {
|
||||
table.push(base.clone());
|
||||
base = base.add(&g, &tmp_params);
|
||||
}
|
||||
|
||||
tables.push(table);
|
||||
num_bits += window;
|
||||
|
||||
for _ in 0..window {
|
||||
g = g.double(&tmp_params);
|
||||
}
|
||||
}
|
||||
|
||||
pedersen_hash_exp.push(tables);
|
||||
}
|
||||
|
||||
tmp_params.pedersen_hash_exp = pedersen_hash_exp;
|
||||
}
|
||||
|
||||
// Create the bases for other parts of the protocol
|
||||
{
|
||||
let mut fixed_base_generators = vec![edwards::Point::zero(); FixedGenerators::Max as usize];
|
||||
|
||||
fixed_base_generators[FixedGenerators::ProofGenerationKey as usize] =
|
||||
find_group_hash(&[], constants::PROOF_GENERATION_KEY_BASE_GENERATOR_PERSONALIZATION, &tmp_params);
|
||||
|
||||
fixed_base_generators[FixedGenerators::NoteCommitmentRandomness as usize] =
|
||||
find_group_hash(b"r", constants::PEDERSEN_HASH_GENERATORS_PERSONALIZATION, &tmp_params);
|
||||
|
||||
fixed_base_generators[FixedGenerators::NullifierPosition as usize] =
|
||||
find_group_hash(&[], constants::NULLIFIER_POSITION_IN_TREE_GENERATOR_PERSONALIZATION, &tmp_params);
|
||||
|
||||
fixed_base_generators[FixedGenerators::ValueCommitmentValue as usize] =
|
||||
find_group_hash(b"v", constants::VALUE_COMMITMENT_GENERATOR_PERSONALIZATION, &tmp_params);
|
||||
|
||||
fixed_base_generators[FixedGenerators::ValueCommitmentRandomness as usize] =
|
||||
find_group_hash(b"r", constants::VALUE_COMMITMENT_GENERATOR_PERSONALIZATION, &tmp_params);
|
||||
|
||||
fixed_base_generators[FixedGenerators::SpendingKeyGenerator as usize] =
|
||||
find_group_hash(&[], constants::SPENDING_KEY_GENERATOR_PERSONALIZATION, &tmp_params);
|
||||
|
||||
// Check for duplicates, far worse than spec inconsistencies!
|
||||
for (i, p1) in fixed_base_generators.iter().enumerate() {
|
||||
if p1 == &edwards::Point::zero() {
|
||||
panic!("Neutral element!");
|
||||
}
|
||||
|
||||
for p2 in fixed_base_generators.iter().skip(i+1) {
|
||||
if p1 == p2 {
|
||||
panic!("Duplicate generator!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tmp_params.fixed_base_generators = fixed_base_generators;
|
||||
}
|
||||
|
||||
// Create the 2-bit window table lookups for each 4-bit
|
||||
// "chunk" in each segment of the Pedersen hash
|
||||
{
|
||||
let mut pedersen_circuit_generators = vec![];
|
||||
|
||||
// Process each segment
|
||||
for mut gen in tmp_params.pedersen_hash_generators.iter().cloned() {
|
||||
let mut gen = montgomery::Point::from_edwards(&gen, &tmp_params);
|
||||
let mut windows = vec![];
|
||||
for _ in 0..tmp_params.pedersen_hash_chunks_per_generator() {
|
||||
// Create (x, y) coeffs for this chunk
|
||||
let mut coeffs = vec![];
|
||||
let mut g = gen.clone();
|
||||
|
||||
// coeffs = g, g*2, g*3, g*4
|
||||
for _ in 0..4 {
|
||||
coeffs.push(g.into_xy().expect("cannot produce O"));
|
||||
g = g.add(&gen, &tmp_params);
|
||||
}
|
||||
windows.push(coeffs);
|
||||
|
||||
// Our chunks are separated by 2 bits to prevent overlap.
|
||||
for _ in 0..4 {
|
||||
gen = gen.double(&tmp_params);
|
||||
}
|
||||
}
|
||||
pedersen_circuit_generators.push(windows);
|
||||
}
|
||||
|
||||
tmp_params.pedersen_circuit_generators = pedersen_circuit_generators;
|
||||
}
|
||||
|
||||
// Create the 3-bit window table lookups for fixed-base
|
||||
// exp of each base in the protocol.
|
||||
{
|
||||
let mut fixed_base_circuit_generators = vec![];
|
||||
|
||||
for mut gen in tmp_params.fixed_base_generators.iter().cloned() {
|
||||
let mut windows = vec![];
|
||||
for _ in 0..tmp_params.fixed_base_chunks_per_generator() {
|
||||
let mut coeffs = vec![(Fr::zero(), Fr::one())];
|
||||
let mut g = gen.clone();
|
||||
for _ in 0..7 {
|
||||
coeffs.push(g.into_xy());
|
||||
g = g.add(&gen, &tmp_params);
|
||||
}
|
||||
windows.push(coeffs);
|
||||
|
||||
// gen = gen * 8
|
||||
gen = g;
|
||||
}
|
||||
fixed_base_circuit_generators.push(windows);
|
||||
}
|
||||
|
||||
tmp_params.fixed_base_circuit_generators = fixed_base_circuit_generators;
|
||||
}
|
||||
|
||||
tmp_params
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_jubjub_bls12() {
|
||||
let params = JubjubBls12::new();
|
||||
|
||||
tests::test_suite::<Bls12>(¶ms);
|
||||
|
||||
let test_repr = hex!("9d12b88b08dcbef8a11ee0712d94cb236ee2f4ca17317075bfafc82ce3139d31");
|
||||
let p = edwards::Point::<Bls12, _>::read(&test_repr[..], ¶ms).unwrap();
|
||||
let q = edwards::Point::<Bls12, _>::get_for_y(
|
||||
Fr::from_str("22440861827555040311190986994816762244378363690614952020532787748720529117853").unwrap(),
|
||||
false,
|
||||
¶ms
|
||||
).unwrap();
|
||||
|
||||
assert!(p == q);
|
||||
|
||||
// Same thing, but sign bit set
|
||||
let test_repr = hex!("9d12b88b08dcbef8a11ee0712d94cb236ee2f4ca17317075bfafc82ce3139db1");
|
||||
let p = edwards::Point::<Bls12, _>::read(&test_repr[..], ¶ms).unwrap();
|
||||
let q = edwards::Point::<Bls12, _>::get_for_y(
|
||||
Fr::from_str("22440861827555040311190986994816762244378363690614952020532787748720529117853").unwrap(),
|
||||
true,
|
||||
¶ms
|
||||
).unwrap();
|
||||
|
||||
assert!(p == q);
|
||||
}
|
351
zcash_primitives/src/jubjub/montgomery.rs
Normal file
351
zcash_primitives/src/jubjub/montgomery.rs
Normal file
@@ -0,0 +1,351 @@
|
||||
use ff::{BitIterator, Field, PrimeField, PrimeFieldRepr, SqrtField};
|
||||
|
||||
use super::{
|
||||
JubjubEngine,
|
||||
JubjubParams,
|
||||
Unknown,
|
||||
PrimeOrder,
|
||||
edwards
|
||||
};
|
||||
|
||||
use rand_core::RngCore;
|
||||
|
||||
use std::marker::PhantomData;
|
||||
|
||||
// Represents the affine point (X, Y)
|
||||
pub struct Point<E: JubjubEngine, Subgroup> {
|
||||
x: E::Fr,
|
||||
y: E::Fr,
|
||||
infinity: bool,
|
||||
_marker: PhantomData<Subgroup>
|
||||
}
|
||||
|
||||
fn convert_subgroup<E: JubjubEngine, S1, S2>(from: &Point<E, S1>) -> Point<E, S2>
|
||||
{
|
||||
Point {
|
||||
x: from.x,
|
||||
y: from.y,
|
||||
infinity: from.infinity,
|
||||
_marker: PhantomData
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: JubjubEngine> From<Point<E, PrimeOrder>> for Point<E, Unknown>
|
||||
{
|
||||
fn from(p: Point<E, PrimeOrder>) -> Point<E, Unknown>
|
||||
{
|
||||
convert_subgroup(&p)
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: JubjubEngine, Subgroup> Clone for Point<E, Subgroup>
|
||||
{
|
||||
fn clone(&self) -> Self {
|
||||
convert_subgroup(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: JubjubEngine, Subgroup> PartialEq for Point<E, Subgroup> {
|
||||
fn eq(&self, other: &Point<E, Subgroup>) -> bool {
|
||||
match (self.infinity, other.infinity) {
|
||||
(true, true) => true,
|
||||
(true, false) | (false, true) => false,
|
||||
(false, false) => {
|
||||
self.x == other.x && self.y == other.y
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: JubjubEngine> Point<E, Unknown> {
|
||||
pub fn get_for_x(x: E::Fr, sign: bool, params: &E::Params) -> Option<Self>
|
||||
{
|
||||
// Given an x on the curve, y = sqrt(x^3 + A*x^2 + x)
|
||||
|
||||
let mut x2 = x;
|
||||
x2.square();
|
||||
|
||||
let mut rhs = x2;
|
||||
rhs.mul_assign(params.montgomery_a());
|
||||
rhs.add_assign(&x);
|
||||
x2.mul_assign(&x);
|
||||
rhs.add_assign(&x2);
|
||||
|
||||
match rhs.sqrt() {
|
||||
Some(mut y) => {
|
||||
if y.into_repr().is_odd() != sign {
|
||||
y.negate();
|
||||
}
|
||||
|
||||
return Some(Point {
|
||||
x: x,
|
||||
y: y,
|
||||
infinity: false,
|
||||
_marker: PhantomData
|
||||
})
|
||||
},
|
||||
None => None
|
||||
}
|
||||
}
|
||||
|
||||
/// This guarantees the point is in the prime order subgroup
|
||||
#[must_use]
|
||||
pub fn mul_by_cofactor(&self, params: &E::Params) -> Point<E, PrimeOrder>
|
||||
{
|
||||
let tmp = self.double(params)
|
||||
.double(params)
|
||||
.double(params);
|
||||
|
||||
convert_subgroup(&tmp)
|
||||
}
|
||||
|
||||
pub fn rand<R: RngCore>(rng: &mut R, params: &E::Params) -> Self
|
||||
{
|
||||
loop {
|
||||
let x = E::Fr::random(rng);
|
||||
let sign = rng.next_u32() % 2 != 0;
|
||||
|
||||
match Self::get_for_x(x, sign, params) {
|
||||
Some(p) => {
|
||||
return p
|
||||
},
|
||||
None => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: JubjubEngine, Subgroup> Point<E, Subgroup> {
|
||||
/// Convert from an Edwards point
|
||||
pub fn from_edwards(
|
||||
e: &edwards::Point<E, Subgroup>,
|
||||
params: &E::Params
|
||||
) -> Self
|
||||
{
|
||||
let (x, y) = e.into_xy();
|
||||
|
||||
if y == E::Fr::one() {
|
||||
// The only solution for y = 1 is x = 0. (0, 1) is
|
||||
// the neutral element, so we map this to the point
|
||||
// at infinity.
|
||||
|
||||
Point::zero()
|
||||
} else {
|
||||
// The map from a twisted Edwards curve is defined as
|
||||
// (x, y) -> (u, v) where
|
||||
// u = (1 + y) / (1 - y)
|
||||
// v = u / x
|
||||
//
|
||||
// This mapping is not defined for y = 1 and for x = 0.
|
||||
//
|
||||
// We have that y != 1 above. If x = 0, the only
|
||||
// solutions for y are 1 (contradiction) or -1.
|
||||
if x.is_zero() {
|
||||
// (0, -1) is the point of order two which is not
|
||||
// the neutral element, so we map it to (0, 0) which is
|
||||
// the only affine point of order 2.
|
||||
|
||||
Point {
|
||||
x: E::Fr::zero(),
|
||||
y: E::Fr::zero(),
|
||||
infinity: false,
|
||||
_marker: PhantomData
|
||||
}
|
||||
} else {
|
||||
// The mapping is defined as above.
|
||||
//
|
||||
// (x, y) -> (u, v) where
|
||||
// u = (1 + y) / (1 - y)
|
||||
// v = u / x
|
||||
|
||||
let mut u = E::Fr::one();
|
||||
u.add_assign(&y);
|
||||
{
|
||||
let mut tmp = E::Fr::one();
|
||||
tmp.sub_assign(&y);
|
||||
u.mul_assign(&tmp.inverse().unwrap())
|
||||
}
|
||||
|
||||
let mut v = u;
|
||||
v.mul_assign(&x.inverse().unwrap());
|
||||
|
||||
// Scale it into the correct curve constants
|
||||
v.mul_assign(params.scale());
|
||||
|
||||
Point {
|
||||
x: u,
|
||||
y: v,
|
||||
infinity: false,
|
||||
_marker: PhantomData
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Attempts to cast this as a prime order element, failing if it's
|
||||
/// not in the prime order subgroup.
|
||||
pub fn as_prime_order(&self, params: &E::Params) -> Option<Point<E, PrimeOrder>> {
|
||||
if self.mul(E::Fs::char(), params) == Point::zero() {
|
||||
Some(convert_subgroup(self))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn zero() -> Self {
|
||||
Point {
|
||||
x: E::Fr::zero(),
|
||||
y: E::Fr::zero(),
|
||||
infinity: true,
|
||||
_marker: PhantomData
|
||||
}
|
||||
}
|
||||
|
||||
pub fn into_xy(&self) -> Option<(E::Fr, E::Fr)>
|
||||
{
|
||||
if self.infinity {
|
||||
None
|
||||
} else {
|
||||
Some((self.x, self.y))
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn negate(&self) -> Self {
|
||||
let mut p = self.clone();
|
||||
|
||||
p.y.negate();
|
||||
|
||||
p
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn double(&self, params: &E::Params) -> Self {
|
||||
if self.infinity {
|
||||
return Point::zero();
|
||||
}
|
||||
|
||||
// (0, 0) is the point of order 2. Doubling
|
||||
// produces the point at infinity.
|
||||
if self.y == E::Fr::zero() {
|
||||
return Point::zero();
|
||||
}
|
||||
|
||||
// This is a standard affine point doubling formula
|
||||
// See 4.3.2 The group law for Weierstrass curves
|
||||
// Montgomery curves and the Montgomery Ladder
|
||||
// Daniel J. Bernstein and Tanja Lange
|
||||
|
||||
let mut delta = E::Fr::one();
|
||||
{
|
||||
let mut tmp = params.montgomery_a().clone();
|
||||
tmp.mul_assign(&self.x);
|
||||
tmp.double();
|
||||
delta.add_assign(&tmp);
|
||||
}
|
||||
{
|
||||
let mut tmp = self.x;
|
||||
tmp.square();
|
||||
delta.add_assign(&tmp);
|
||||
tmp.double();
|
||||
delta.add_assign(&tmp);
|
||||
}
|
||||
{
|
||||
let mut tmp = self.y;
|
||||
tmp.double();
|
||||
delta.mul_assign(&tmp.inverse().expect("y is nonzero so this must be nonzero"));
|
||||
}
|
||||
|
||||
let mut x3 = delta;
|
||||
x3.square();
|
||||
x3.sub_assign(params.montgomery_a());
|
||||
x3.sub_assign(&self.x);
|
||||
x3.sub_assign(&self.x);
|
||||
|
||||
let mut y3 = x3;
|
||||
y3.sub_assign(&self.x);
|
||||
y3.mul_assign(&delta);
|
||||
y3.add_assign(&self.y);
|
||||
y3.negate();
|
||||
|
||||
Point {
|
||||
x: x3,
|
||||
y: y3,
|
||||
infinity: false,
|
||||
_marker: PhantomData
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn add(&self, other: &Self, params: &E::Params) -> Self
|
||||
{
|
||||
// This is a standard affine point addition formula
|
||||
// See 4.3.2 The group law for Weierstrass curves
|
||||
// Montgomery curves and the Montgomery Ladder
|
||||
// Daniel J. Bernstein and Tanja Lange
|
||||
|
||||
match (self.infinity, other.infinity) {
|
||||
(true, true) => Point::zero(),
|
||||
(true, false) => other.clone(),
|
||||
(false, true) => self.clone(),
|
||||
(false, false) => {
|
||||
if self.x == other.x {
|
||||
if self.y == other.y {
|
||||
self.double(params)
|
||||
} else {
|
||||
Point::zero()
|
||||
}
|
||||
} else {
|
||||
let mut delta = other.y;
|
||||
delta.sub_assign(&self.y);
|
||||
{
|
||||
let mut tmp = other.x;
|
||||
tmp.sub_assign(&self.x);
|
||||
delta.mul_assign(&tmp.inverse().expect("self.x != other.x, so this must be nonzero"));
|
||||
}
|
||||
|
||||
let mut x3 = delta;
|
||||
x3.square();
|
||||
x3.sub_assign(params.montgomery_a());
|
||||
x3.sub_assign(&self.x);
|
||||
x3.sub_assign(&other.x);
|
||||
|
||||
let mut y3 = x3;
|
||||
y3.sub_assign(&self.x);
|
||||
y3.mul_assign(&delta);
|
||||
y3.add_assign(&self.y);
|
||||
y3.negate();
|
||||
|
||||
Point {
|
||||
x: x3,
|
||||
y: y3,
|
||||
infinity: false,
|
||||
_marker: PhantomData
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn mul<S: Into<<E::Fs as PrimeField>::Repr>>(
|
||||
&self,
|
||||
scalar: S,
|
||||
params: &E::Params
|
||||
) -> Self
|
||||
{
|
||||
// Standard double-and-add scalar multiplication
|
||||
|
||||
let mut res = Self::zero();
|
||||
|
||||
for b in BitIterator::new(scalar.into()) {
|
||||
res = res.double(params);
|
||||
|
||||
if b {
|
||||
res = res.add(self, params);
|
||||
}
|
||||
}
|
||||
|
||||
res
|
||||
}
|
||||
}
|
444
zcash_primitives/src/jubjub/tests.rs
Normal file
444
zcash_primitives/src/jubjub/tests.rs
Normal file
@@ -0,0 +1,444 @@
|
||||
use super::{
|
||||
JubjubEngine,
|
||||
JubjubParams,
|
||||
PrimeOrder,
|
||||
montgomery,
|
||||
edwards
|
||||
};
|
||||
|
||||
use ff::{
|
||||
Field,
|
||||
PrimeField,
|
||||
PrimeFieldRepr,
|
||||
SqrtField,
|
||||
LegendreSymbol
|
||||
};
|
||||
|
||||
use rand_core::{RngCore, SeedableRng};
|
||||
use rand_xorshift::XorShiftRng;
|
||||
|
||||
pub fn test_suite<E: JubjubEngine>(params: &E::Params) {
|
||||
test_back_and_forth::<E>(params);
|
||||
test_jubjub_params::<E>(params);
|
||||
test_rand::<E>(params);
|
||||
test_get_for::<E>(params);
|
||||
test_identities::<E>(params);
|
||||
test_addition_associativity::<E>(params);
|
||||
test_order::<E>(params);
|
||||
test_mul_associativity::<E>(params);
|
||||
test_loworder::<E>(params);
|
||||
test_read_write::<E>(params);
|
||||
}
|
||||
|
||||
fn is_on_mont_curve<E: JubjubEngine, P: JubjubParams<E>>(
|
||||
x: E::Fr,
|
||||
y: E::Fr,
|
||||
params: &P
|
||||
) -> bool
|
||||
{
|
||||
let mut lhs = y;
|
||||
lhs.square();
|
||||
|
||||
let mut x2 = x;
|
||||
x2.square();
|
||||
|
||||
let mut x3 = x2;
|
||||
x3.mul_assign(&x);
|
||||
|
||||
let mut rhs = x2;
|
||||
rhs.mul_assign(params.montgomery_a());
|
||||
rhs.add_assign(&x);
|
||||
rhs.add_assign(&x3);
|
||||
|
||||
lhs == rhs
|
||||
}
|
||||
|
||||
fn is_on_twisted_edwards_curve<E: JubjubEngine, P: JubjubParams<E>>(
|
||||
x: E::Fr,
|
||||
y: E::Fr,
|
||||
params: &P
|
||||
) -> bool
|
||||
{
|
||||
let mut x2 = x;
|
||||
x2.square();
|
||||
|
||||
let mut y2 = y;
|
||||
y2.square();
|
||||
|
||||
// -x^2 + y^2
|
||||
let mut lhs = y2;
|
||||
lhs.sub_assign(&x2);
|
||||
|
||||
// 1 + d x^2 y^2
|
||||
let mut rhs = y2;
|
||||
rhs.mul_assign(&x2);
|
||||
rhs.mul_assign(params.edwards_d());
|
||||
rhs.add_assign(&E::Fr::one());
|
||||
|
||||
lhs == rhs
|
||||
}
|
||||
|
||||
fn test_loworder<E: JubjubEngine>(params: &E::Params) {
|
||||
let rng = &mut XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||
0xe5,
|
||||
]);
|
||||
let inf = montgomery::Point::zero();
|
||||
|
||||
// try to find a point of order 8
|
||||
let p = loop {
|
||||
let r = montgomery::Point::<E, _>::rand(rng, params).mul(E::Fs::char(), params);
|
||||
|
||||
let r2 = r.double(params);
|
||||
let r4 = r2.double(params);
|
||||
let r8 = r4.double(params);
|
||||
|
||||
if r2 != inf && r4 != inf && r8 == inf {
|
||||
break r;
|
||||
}
|
||||
};
|
||||
|
||||
let mut loworder_points = vec![];
|
||||
{
|
||||
let mut tmp = p.clone();
|
||||
|
||||
for _ in 0..8 {
|
||||
assert!(!loworder_points.contains(&tmp));
|
||||
loworder_points.push(tmp.clone());
|
||||
tmp = tmp.add(&p, params);
|
||||
}
|
||||
}
|
||||
assert!(loworder_points[7] == inf);
|
||||
}
|
||||
|
||||
fn test_mul_associativity<E: JubjubEngine>(params: &E::Params) {
|
||||
use self::edwards::Point;
|
||||
let rng = &mut XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||
0xe5,
|
||||
]);
|
||||
|
||||
for _ in 0..100 {
|
||||
// Pick a random point and multiply it by the cofactor
|
||||
let base = Point::<E, _>::rand(rng, params).mul_by_cofactor(params);
|
||||
|
||||
let mut a = E::Fs::random(rng);
|
||||
let b = E::Fs::random(rng);
|
||||
let c = E::Fs::random(rng);
|
||||
|
||||
let res1 = base.mul(a, params).mul(b, params).mul(c, params);
|
||||
let res2 = base.mul(b, params).mul(c, params).mul(a, params);
|
||||
let res3 = base.mul(c, params).mul(a, params).mul(b, params);
|
||||
a.mul_assign(&b);
|
||||
a.mul_assign(&c);
|
||||
let res4 = base.mul(a, params);
|
||||
|
||||
assert!(res1 == res2);
|
||||
assert!(res2 == res3);
|
||||
assert!(res3 == res4);
|
||||
|
||||
let (x, y) = res1.into_xy();
|
||||
assert!(is_on_twisted_edwards_curve(x, y, params));
|
||||
|
||||
let (x, y) = res2.into_xy();
|
||||
assert!(is_on_twisted_edwards_curve(x, y, params));
|
||||
|
||||
let (x, y) = res3.into_xy();
|
||||
assert!(is_on_twisted_edwards_curve(x, y, params));
|
||||
}
|
||||
}
|
||||
|
||||
fn test_order<E: JubjubEngine>(params: &E::Params) {
|
||||
use self::edwards::Point;
|
||||
let rng = &mut XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||
0xe5,
|
||||
]);
|
||||
|
||||
// The neutral element is in the prime order subgroup.
|
||||
assert!(Point::<E, PrimeOrder>::zero().as_prime_order(params).is_some());
|
||||
|
||||
for _ in 0..50 {
|
||||
// Pick a random point and multiply it by the cofactor
|
||||
let base = Point::<E, _>::rand(rng, params).mul_by_cofactor(params);
|
||||
|
||||
// Any point multiplied by the cofactor will be in the prime
|
||||
// order subgroup
|
||||
assert!(base.as_prime_order(params).is_some());
|
||||
}
|
||||
|
||||
// It's very likely that at least one out of 50 random points on the curve
|
||||
// is not in the prime order subgroup.
|
||||
let mut at_least_one_not_in_prime_order_subgroup = false;
|
||||
for _ in 0..50 {
|
||||
// Pick a random point.
|
||||
let base = Point::<E, _>::rand(rng, params);
|
||||
|
||||
at_least_one_not_in_prime_order_subgroup |= base.as_prime_order(params).is_none();
|
||||
}
|
||||
assert!(at_least_one_not_in_prime_order_subgroup);
|
||||
}
|
||||
|
||||
fn test_addition_associativity<E: JubjubEngine>(params: &E::Params) {
|
||||
let rng = &mut XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||
0xe5,
|
||||
]);
|
||||
|
||||
for _ in 0..1000 {
|
||||
use self::montgomery::Point;
|
||||
|
||||
let a = Point::<E, _>::rand(rng, params);
|
||||
let b = Point::<E, _>::rand(rng, params);
|
||||
let c = Point::<E, _>::rand(rng, params);
|
||||
|
||||
assert!(a.add(&b, ¶ms).add(&c, ¶ms) == c.add(&a, ¶ms).add(&b, ¶ms));
|
||||
}
|
||||
|
||||
for _ in 0..1000 {
|
||||
use self::edwards::Point;
|
||||
|
||||
let a = Point::<E, _>::rand(rng, params);
|
||||
let b = Point::<E, _>::rand(rng, params);
|
||||
let c = Point::<E, _>::rand(rng, params);
|
||||
|
||||
assert!(a.add(&b, ¶ms).add(&c, ¶ms) == c.add(&a, ¶ms).add(&b, ¶ms));
|
||||
}
|
||||
}
|
||||
|
||||
fn test_identities<E: JubjubEngine>(params: &E::Params) {
|
||||
let rng = &mut XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||
0xe5,
|
||||
]);
|
||||
|
||||
{
|
||||
use self::edwards::Point;
|
||||
|
||||
let z = Point::<E, PrimeOrder>::zero();
|
||||
assert!(z.double(¶ms) == z);
|
||||
assert!(z.negate() == z);
|
||||
|
||||
for _ in 0..100 {
|
||||
let r = Point::<E, _>::rand(rng, params);
|
||||
|
||||
assert!(r.add(&Point::zero(), ¶ms) == r);
|
||||
assert!(r.add(&r.negate(), ¶ms) == Point::zero());
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
use self::montgomery::Point;
|
||||
|
||||
let z = Point::<E, PrimeOrder>::zero();
|
||||
assert!(z.double(¶ms) == z);
|
||||
assert!(z.negate() == z);
|
||||
|
||||
for _ in 0..100 {
|
||||
let r = Point::<E, _>::rand(rng, params);
|
||||
|
||||
assert!(r.add(&Point::zero(), ¶ms) == r);
|
||||
assert!(r.add(&r.negate(), ¶ms) == Point::zero());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_get_for<E: JubjubEngine>(params: &E::Params) {
|
||||
let rng = &mut XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||
0xe5,
|
||||
]);
|
||||
|
||||
for _ in 0..1000 {
|
||||
let y = E::Fr::random(rng);
|
||||
let sign = rng.next_u32() % 2 == 1;
|
||||
|
||||
if let Some(mut p) = edwards::Point::<E, _>::get_for_y(y, sign, params) {
|
||||
assert!(p.into_xy().0.into_repr().is_odd() == sign);
|
||||
p = p.negate();
|
||||
assert!(
|
||||
edwards::Point::<E, _>::get_for_y(y, !sign, params).unwrap()
|
||||
==
|
||||
p
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_read_write<E: JubjubEngine>(params: &E::Params) {
|
||||
let rng = &mut XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||
0xe5,
|
||||
]);
|
||||
|
||||
for _ in 0..1000 {
|
||||
let e = edwards::Point::<E, _>::rand(rng, params);
|
||||
|
||||
let mut v = vec![];
|
||||
e.write(&mut v).unwrap();
|
||||
|
||||
let e2 = edwards::Point::read(&v[..], params).unwrap();
|
||||
|
||||
assert!(e == e2);
|
||||
}
|
||||
}
|
||||
|
||||
fn test_rand<E: JubjubEngine>(params: &E::Params) {
|
||||
let rng = &mut XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||
0xe5,
|
||||
]);
|
||||
|
||||
for _ in 0..1000 {
|
||||
let p = montgomery::Point::<E, _>::rand(rng, params);
|
||||
let e = edwards::Point::<E, _>::rand(rng, params);
|
||||
|
||||
{
|
||||
let (x, y) = p.into_xy().unwrap();
|
||||
assert!(is_on_mont_curve(x, y, params));
|
||||
}
|
||||
|
||||
{
|
||||
let (x, y) = e.into_xy();
|
||||
assert!(is_on_twisted_edwards_curve(x, y, params));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_back_and_forth<E: JubjubEngine>(params: &E::Params) {
|
||||
let rng = &mut XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x3d, 0x76, 0x5d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||
0xe5,
|
||||
]);
|
||||
|
||||
for _ in 0..1000 {
|
||||
let s = E::Fs::random(rng);
|
||||
let edwards_p1 = edwards::Point::<E, _>::rand(rng, params);
|
||||
let mont_p1 = montgomery::Point::from_edwards(&edwards_p1, params);
|
||||
let mont_p2 = montgomery::Point::<E, _>::rand(rng, params);
|
||||
let edwards_p2 = edwards::Point::from_montgomery(&mont_p2, params);
|
||||
|
||||
let mont = mont_p1.add(&mont_p2, params).mul(s, params);
|
||||
let edwards = edwards_p1.add(&edwards_p2, params).mul(s, params);
|
||||
|
||||
assert!(
|
||||
montgomery::Point::from_edwards(&edwards, params) == mont
|
||||
);
|
||||
|
||||
assert!(
|
||||
edwards::Point::from_montgomery(&mont, params) == edwards
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn test_jubjub_params<E: JubjubEngine>(params: &E::Params) {
|
||||
// a = -1
|
||||
let mut a = E::Fr::one();
|
||||
a.negate();
|
||||
|
||||
{
|
||||
// Check that 2A is consistent with A
|
||||
let mut tmp = *params.montgomery_a();
|
||||
tmp.double();
|
||||
|
||||
assert_eq!(&tmp, params.montgomery_2a());
|
||||
}
|
||||
|
||||
{
|
||||
// The twisted Edwards addition law is complete when d is nonsquare
|
||||
// and a is square.
|
||||
|
||||
assert!(params.edwards_d().legendre() == LegendreSymbol::QuadraticNonResidue);
|
||||
assert!(a.legendre() == LegendreSymbol::QuadraticResidue);
|
||||
}
|
||||
|
||||
{
|
||||
// Other convenient sanity checks regarding d
|
||||
|
||||
// tmp = d
|
||||
let mut tmp = *params.edwards_d();
|
||||
|
||||
// 1 / d is nonsquare
|
||||
assert!(tmp.inverse().unwrap().legendre() == LegendreSymbol::QuadraticNonResidue);
|
||||
|
||||
// tmp = -d
|
||||
tmp.negate();
|
||||
|
||||
// -d is nonsquare
|
||||
assert!(tmp.legendre() == LegendreSymbol::QuadraticNonResidue);
|
||||
|
||||
// 1 / -d is nonsquare
|
||||
assert!(tmp.inverse().unwrap().legendre() == LegendreSymbol::QuadraticNonResidue);
|
||||
}
|
||||
|
||||
{
|
||||
// Check that A^2 - 4 is nonsquare:
|
||||
let mut tmp = params.montgomery_a().clone();
|
||||
tmp.square();
|
||||
tmp.sub_assign(&E::Fr::from_str("4").unwrap());
|
||||
assert!(tmp.legendre() == LegendreSymbol::QuadraticNonResidue);
|
||||
}
|
||||
|
||||
{
|
||||
// Check that A - 2 is nonsquare:
|
||||
let mut tmp = params.montgomery_a().clone();
|
||||
tmp.sub_assign(&E::Fr::from_str("2").unwrap());
|
||||
assert!(tmp.legendre() == LegendreSymbol::QuadraticNonResidue);
|
||||
}
|
||||
|
||||
{
|
||||
// Check the validity of the scaling factor
|
||||
let mut tmp = a;
|
||||
tmp.sub_assign(¶ms.edwards_d());
|
||||
tmp = tmp.inverse().unwrap();
|
||||
tmp.mul_assign(&E::Fr::from_str("4").unwrap());
|
||||
tmp = tmp.sqrt().unwrap();
|
||||
assert_eq!(&tmp, params.scale());
|
||||
}
|
||||
|
||||
{
|
||||
// Check that the number of windows per generator
|
||||
// in the Pedersen hash does not allow for collisions
|
||||
|
||||
let mut cur = E::Fs::one().into_repr();
|
||||
|
||||
let mut max = E::Fs::char();
|
||||
{
|
||||
max.sub_noborrow(&E::Fs::one().into_repr());
|
||||
max.div2();
|
||||
}
|
||||
|
||||
let mut pacc = E::Fs::zero().into_repr();
|
||||
let mut nacc = E::Fs::char();
|
||||
|
||||
for _ in 0..params.pedersen_hash_chunks_per_generator()
|
||||
{
|
||||
// tmp = cur * 4
|
||||
let mut tmp = cur;
|
||||
tmp.mul2();
|
||||
tmp.mul2();
|
||||
|
||||
pacc.add_nocarry(&tmp);
|
||||
nacc.sub_noborrow(&tmp);
|
||||
|
||||
assert!(pacc < max);
|
||||
assert!(pacc < nacc);
|
||||
|
||||
// cur = cur * 16
|
||||
for _ in 0..4 {
|
||||
cur.mul2();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// Check that the number of windows for fixed-base
|
||||
// scalar multiplication is sufficient for all scalars.
|
||||
|
||||
assert!(params.fixed_base_chunks_per_generator() * 3 >= E::Fs::NUM_BITS as usize);
|
||||
|
||||
// ... and that it's *just* efficient enough.
|
||||
|
||||
assert!((params.fixed_base_chunks_per_generator() - 1) * 3 < E::Fs::NUM_BITS as usize);
|
||||
}
|
||||
}
|
@@ -4,7 +4,7 @@
|
||||
|
||||
use blake2b_simd::{Hash as Blake2bHash, Params as Blake2bParams};
|
||||
use ff::{PrimeField, PrimeFieldRepr};
|
||||
use sapling_crypto::{
|
||||
use crate::{
|
||||
jubjub::{edwards, FixedGenerators, JubjubEngine, JubjubParams, ToUniform, Unknown},
|
||||
primitives::{ProofGenerationKey, ViewingKey},
|
||||
};
|
||||
@@ -188,7 +188,7 @@ impl<E: JubjubEngine> FullViewingKey<E> {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use pairing::bls12_381::Bls12;
|
||||
use sapling_crypto::jubjub::{edwards, FixedGenerators, JubjubParams, PrimeOrder};
|
||||
use crate::jubjub::{edwards, FixedGenerators, JubjubParams, PrimeOrder};
|
||||
use std::error::Error;
|
||||
|
||||
use super::FullViewingKey;
|
||||
|
@@ -3,6 +3,7 @@ extern crate lazy_static;
|
||||
|
||||
extern crate aes;
|
||||
extern crate blake2b_simd;
|
||||
extern crate blake2s_simd;
|
||||
extern crate byteorder;
|
||||
extern crate crypto_api_chachapoly;
|
||||
extern crate ff;
|
||||
@@ -12,19 +13,25 @@ extern crate pairing;
|
||||
extern crate rand;
|
||||
extern crate rand_core;
|
||||
extern crate rand_os;
|
||||
extern crate sapling_crypto;
|
||||
extern crate sha2;
|
||||
|
||||
#[cfg(test)]
|
||||
#[macro_use]
|
||||
extern crate hex_literal;
|
||||
|
||||
#[cfg(test)]
|
||||
extern crate rand_xorshift;
|
||||
|
||||
use sapling_crypto::jubjub::JubjubBls12;
|
||||
|
||||
pub mod block;
|
||||
pub mod constants;
|
||||
pub mod group_hash;
|
||||
pub mod jubjub;
|
||||
pub mod keys;
|
||||
pub mod legacy;
|
||||
pub mod merkle_tree;
|
||||
pub mod note_encryption;
|
||||
pub mod pedersen_hash;
|
||||
pub mod primitives;
|
||||
pub mod prover;
|
||||
pub mod redjubjub;
|
||||
pub mod sapling;
|
||||
@@ -36,6 +43,8 @@ pub mod zip32;
|
||||
#[cfg(test)]
|
||||
mod test_vectors;
|
||||
|
||||
use jubjub::JubjubBls12;
|
||||
|
||||
lazy_static! {
|
||||
pub static ref JUBJUB: JubjubBls12 = { JubjubBls12::new() };
|
||||
}
|
||||
|
@@ -6,7 +6,7 @@ use crypto_api_chachapoly::{ChaCha20Ietf, ChachaPolyIetf};
|
||||
use ff::{PrimeField, PrimeFieldRepr};
|
||||
use pairing::bls12_381::{Bls12, Fr};
|
||||
use rand_core::{CryptoRng, RngCore};
|
||||
use sapling_crypto::{
|
||||
use crate::{
|
||||
jubjub::{
|
||||
edwards,
|
||||
fs::{Fs, FsRepr},
|
||||
@@ -211,18 +211,16 @@ fn prf_ock(
|
||||
/// extern crate ff;
|
||||
/// extern crate pairing;
|
||||
/// extern crate rand_os;
|
||||
/// extern crate sapling_crypto;
|
||||
/// extern crate zcash_primitives;
|
||||
///
|
||||
/// use ff::Field;
|
||||
/// use pairing::bls12_381::Bls12;
|
||||
/// use rand_os::OsRng;
|
||||
/// use sapling_crypto::{
|
||||
/// jubjub::fs::Fs,
|
||||
/// primitives::{Diversifier, PaymentAddress, ValueCommitment},
|
||||
/// };
|
||||
/// use zcash_primitives::{
|
||||
/// jubjub::fs::Fs,
|
||||
/// keys::OutgoingViewingKey,
|
||||
/// note_encryption::{Memo, SaplingNoteEncryption},
|
||||
/// primitives::{Diversifier, PaymentAddress, ValueCommitment},
|
||||
/// JUBJUB,
|
||||
/// };
|
||||
///
|
||||
@@ -551,7 +549,7 @@ mod tests {
|
||||
use pairing::bls12_381::{Bls12, Fr, FrRepr};
|
||||
use rand_core::{CryptoRng, RngCore};
|
||||
use rand_os::OsRng;
|
||||
use sapling_crypto::{
|
||||
use crate::{
|
||||
jubjub::{
|
||||
edwards,
|
||||
fs::{Fs, FsRepr},
|
||||
|
103
zcash_primitives/src/pedersen_hash.rs
Normal file
103
zcash_primitives/src/pedersen_hash.rs
Normal file
@@ -0,0 +1,103 @@
|
||||
use ff::{Field, PrimeField, PrimeFieldRepr};
|
||||
use jubjub::*;
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub enum Personalization {
|
||||
NoteCommitment,
|
||||
MerkleTree(usize)
|
||||
}
|
||||
|
||||
impl Personalization {
|
||||
pub fn get_bits(&self) -> Vec<bool> {
|
||||
match *self {
|
||||
Personalization::NoteCommitment =>
|
||||
vec![true, true, true, true, true, true],
|
||||
Personalization::MerkleTree(num) => {
|
||||
assert!(num < 63);
|
||||
|
||||
(0..6).map(|i| (num >> i) & 1 == 1).collect()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pedersen_hash<E, I>(
|
||||
personalization: Personalization,
|
||||
bits: I,
|
||||
params: &E::Params
|
||||
) -> edwards::Point<E, PrimeOrder>
|
||||
where I: IntoIterator<Item=bool>,
|
||||
E: JubjubEngine
|
||||
{
|
||||
let mut bits = personalization.get_bits().into_iter().chain(bits.into_iter());
|
||||
|
||||
let mut result = edwards::Point::zero();
|
||||
let mut generators = params.pedersen_hash_exp_table().iter();
|
||||
|
||||
loop {
|
||||
let mut acc = E::Fs::zero();
|
||||
let mut cur = E::Fs::one();
|
||||
let mut chunks_remaining = params.pedersen_hash_chunks_per_generator();
|
||||
let mut encountered_bits = false;
|
||||
|
||||
// Grab three bits from the input
|
||||
while let Some(a) = bits.next() {
|
||||
encountered_bits = true;
|
||||
|
||||
let b = bits.next().unwrap_or(false);
|
||||
let c = bits.next().unwrap_or(false);
|
||||
|
||||
// Start computing this portion of the scalar
|
||||
let mut tmp = cur;
|
||||
if a {
|
||||
tmp.add_assign(&cur);
|
||||
}
|
||||
cur.double(); // 2^1 * cur
|
||||
if b {
|
||||
tmp.add_assign(&cur);
|
||||
}
|
||||
|
||||
// conditionally negate
|
||||
if c {
|
||||
tmp.negate();
|
||||
}
|
||||
|
||||
acc.add_assign(&tmp);
|
||||
|
||||
chunks_remaining -= 1;
|
||||
|
||||
if chunks_remaining == 0 {
|
||||
break;
|
||||
} else {
|
||||
cur.double(); // 2^2 * cur
|
||||
cur.double(); // 2^3 * cur
|
||||
cur.double(); // 2^4 * cur
|
||||
}
|
||||
}
|
||||
|
||||
if !encountered_bits {
|
||||
break;
|
||||
}
|
||||
|
||||
let mut table: &[Vec<edwards::Point<E, _>>] = &generators.next().expect("we don't have enough generators");
|
||||
let window = JubjubBls12::pedersen_hash_exp_window_size();
|
||||
let window_mask = (1 << window) - 1;
|
||||
|
||||
let mut acc = acc.into_repr();
|
||||
|
||||
let mut tmp = edwards::Point::zero();
|
||||
|
||||
while !acc.is_zero() {
|
||||
let i = (acc.as_ref()[0] & window_mask) as usize;
|
||||
|
||||
tmp = tmp.add(&table[0][i], params);
|
||||
|
||||
acc.shr(window);
|
||||
table = &table[1..];
|
||||
}
|
||||
|
||||
result = result.add(&tmp, params);
|
||||
}
|
||||
|
||||
result
|
||||
}
|
276
zcash_primitives/src/primitives.rs
Normal file
276
zcash_primitives/src/primitives.rs
Normal file
@@ -0,0 +1,276 @@
|
||||
use ff::{Field, PrimeField, PrimeFieldRepr};
|
||||
|
||||
use constants;
|
||||
|
||||
use group_hash::group_hash;
|
||||
|
||||
use pedersen_hash::{
|
||||
pedersen_hash,
|
||||
Personalization
|
||||
};
|
||||
|
||||
use byteorder::{
|
||||
LittleEndian,
|
||||
WriteBytesExt
|
||||
};
|
||||
|
||||
use jubjub::{
|
||||
JubjubEngine,
|
||||
JubjubParams,
|
||||
edwards,
|
||||
PrimeOrder,
|
||||
FixedGenerators
|
||||
};
|
||||
|
||||
use blake2s_simd::Params as Blake2sParams;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ValueCommitment<E: JubjubEngine> {
|
||||
pub value: u64,
|
||||
pub randomness: E::Fs
|
||||
}
|
||||
|
||||
impl<E: JubjubEngine> ValueCommitment<E> {
|
||||
pub fn cm(
|
||||
&self,
|
||||
params: &E::Params
|
||||
) -> edwards::Point<E, PrimeOrder>
|
||||
{
|
||||
params.generator(FixedGenerators::ValueCommitmentValue)
|
||||
.mul(self.value, params)
|
||||
.add(
|
||||
¶ms.generator(FixedGenerators::ValueCommitmentRandomness)
|
||||
.mul(self.randomness, params),
|
||||
params
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ProofGenerationKey<E: JubjubEngine> {
|
||||
pub ak: edwards::Point<E, PrimeOrder>,
|
||||
pub nsk: E::Fs
|
||||
}
|
||||
|
||||
impl<E: JubjubEngine> ProofGenerationKey<E> {
|
||||
pub fn into_viewing_key(&self, params: &E::Params) -> ViewingKey<E> {
|
||||
ViewingKey {
|
||||
ak: self.ak.clone(),
|
||||
nk: params.generator(FixedGenerators::ProofGenerationKey)
|
||||
.mul(self.nsk, params)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ViewingKey<E: JubjubEngine> {
|
||||
pub ak: edwards::Point<E, PrimeOrder>,
|
||||
pub nk: edwards::Point<E, PrimeOrder>
|
||||
}
|
||||
|
||||
impl<E: JubjubEngine> ViewingKey<E> {
|
||||
pub fn rk(
|
||||
&self,
|
||||
ar: E::Fs,
|
||||
params: &E::Params
|
||||
) -> edwards::Point<E, PrimeOrder> {
|
||||
self.ak.add(
|
||||
¶ms.generator(FixedGenerators::SpendingKeyGenerator)
|
||||
.mul(ar, params),
|
||||
params
|
||||
)
|
||||
}
|
||||
|
||||
pub fn ivk(&self) -> E::Fs {
|
||||
let mut preimage = [0; 64];
|
||||
|
||||
self.ak.write(&mut preimage[0..32]).unwrap();
|
||||
self.nk.write(&mut preimage[32..64]).unwrap();
|
||||
|
||||
let mut h = [0; 32];
|
||||
h.copy_from_slice(Blake2sParams::new()
|
||||
.hash_length(32)
|
||||
.personal(constants::CRH_IVK_PERSONALIZATION)
|
||||
.hash(&preimage)
|
||||
.as_bytes());
|
||||
|
||||
// Drop the most significant five bits, so it can be interpreted as a scalar.
|
||||
h[31] &= 0b0000_0111;
|
||||
|
||||
let mut e = <E::Fs as PrimeField>::Repr::default();
|
||||
e.read_le(&h[..]).unwrap();
|
||||
|
||||
E::Fs::from_repr(e).expect("should be a valid scalar")
|
||||
}
|
||||
|
||||
pub fn into_payment_address(
|
||||
&self,
|
||||
diversifier: Diversifier,
|
||||
params: &E::Params
|
||||
) -> Option<PaymentAddress<E>>
|
||||
{
|
||||
diversifier.g_d(params).map(|g_d| {
|
||||
let pk_d = g_d.mul(self.ivk(), params);
|
||||
|
||||
PaymentAddress {
|
||||
pk_d: pk_d,
|
||||
diversifier: diversifier
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub struct Diversifier(pub [u8; 11]);
|
||||
|
||||
impl Diversifier {
|
||||
pub fn g_d<E: JubjubEngine>(
|
||||
&self,
|
||||
params: &E::Params
|
||||
) -> Option<edwards::Point<E, PrimeOrder>>
|
||||
{
|
||||
group_hash::<E>(&self.0, constants::KEY_DIVERSIFICATION_PERSONALIZATION, params)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct PaymentAddress<E: JubjubEngine> {
|
||||
pub pk_d: edwards::Point<E, PrimeOrder>,
|
||||
pub diversifier: Diversifier
|
||||
}
|
||||
|
||||
impl<E: JubjubEngine> PartialEq for PaymentAddress<E> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.pk_d == other.pk_d && self.diversifier == other.diversifier
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: JubjubEngine> PaymentAddress<E> {
|
||||
pub fn g_d(
|
||||
&self,
|
||||
params: &E::Params
|
||||
) -> Option<edwards::Point<E, PrimeOrder>>
|
||||
{
|
||||
self.diversifier.g_d(params)
|
||||
}
|
||||
|
||||
pub fn create_note(
|
||||
&self,
|
||||
value: u64,
|
||||
randomness: E::Fs,
|
||||
params: &E::Params
|
||||
) -> Option<Note<E>>
|
||||
{
|
||||
self.g_d(params).map(|g_d| {
|
||||
Note {
|
||||
value: value,
|
||||
r: randomness,
|
||||
g_d: g_d,
|
||||
pk_d: self.pk_d.clone()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Note<E: JubjubEngine> {
|
||||
/// The value of the note
|
||||
pub value: u64,
|
||||
/// The diversified base of the address, GH(d)
|
||||
pub g_d: edwards::Point<E, PrimeOrder>,
|
||||
/// The public key of the address, g_d^ivk
|
||||
pub pk_d: edwards::Point<E, PrimeOrder>,
|
||||
/// The commitment randomness
|
||||
pub r: E::Fs
|
||||
}
|
||||
|
||||
impl<E: JubjubEngine> PartialEq for Note<E> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.value == other.value
|
||||
&& self.g_d == other.g_d
|
||||
&& self.pk_d == other.pk_d
|
||||
&& self.r == other.r
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: JubjubEngine> Note<E> {
|
||||
pub fn uncommitted() -> E::Fr {
|
||||
// The smallest u-coordinate that is not on the curve
|
||||
// is one.
|
||||
// TODO: This should be relocated to JubjubEngine as
|
||||
// it's specific to the curve we're using, not all
|
||||
// twisted edwards curves.
|
||||
E::Fr::one()
|
||||
}
|
||||
|
||||
/// Computes the note commitment, returning the full point.
|
||||
fn cm_full_point(&self, params: &E::Params) -> edwards::Point<E, PrimeOrder>
|
||||
{
|
||||
// Calculate the note contents, as bytes
|
||||
let mut note_contents = vec![];
|
||||
|
||||
// Writing the value in little endian
|
||||
(&mut note_contents).write_u64::<LittleEndian>(self.value).unwrap();
|
||||
|
||||
// Write g_d
|
||||
self.g_d.write(&mut note_contents).unwrap();
|
||||
|
||||
// Write pk_d
|
||||
self.pk_d.write(&mut note_contents).unwrap();
|
||||
|
||||
assert_eq!(note_contents.len(), 32 + 32 + 8);
|
||||
|
||||
// Compute the Pedersen hash of the note contents
|
||||
let hash_of_contents = pedersen_hash(
|
||||
Personalization::NoteCommitment,
|
||||
note_contents.into_iter()
|
||||
.flat_map(|byte| {
|
||||
(0..8).map(move |i| ((byte >> i) & 1) == 1)
|
||||
}),
|
||||
params
|
||||
);
|
||||
|
||||
// Compute final commitment
|
||||
params.generator(FixedGenerators::NoteCommitmentRandomness)
|
||||
.mul(self.r, params)
|
||||
.add(&hash_of_contents, params)
|
||||
}
|
||||
|
||||
/// Computes the nullifier given the viewing key and
|
||||
/// note position
|
||||
pub fn nf(
|
||||
&self,
|
||||
viewing_key: &ViewingKey<E>,
|
||||
position: u64,
|
||||
params: &E::Params
|
||||
) -> Vec<u8>
|
||||
{
|
||||
// Compute rho = cm + position.G
|
||||
let rho = self
|
||||
.cm_full_point(params)
|
||||
.add(
|
||||
¶ms.generator(FixedGenerators::NullifierPosition)
|
||||
.mul(position, params),
|
||||
params
|
||||
);
|
||||
|
||||
// Compute nf = BLAKE2s(nk | rho)
|
||||
let mut nf_preimage = [0u8; 64];
|
||||
viewing_key.nk.write(&mut nf_preimage[0..32]).unwrap();
|
||||
rho.write(&mut nf_preimage[32..64]).unwrap();
|
||||
Blake2sParams::new()
|
||||
.hash_length(32)
|
||||
.personal(constants::PRF_NF_PERSONALIZATION)
|
||||
.hash(&nf_preimage)
|
||||
.as_bytes()
|
||||
.to_vec()
|
||||
}
|
||||
|
||||
/// Computes the note commitment
|
||||
pub fn cm(&self, params: &E::Params) -> E::Fr
|
||||
{
|
||||
// The commitment is in the prime order subgroup, so mapping the
|
||||
// commitment to the x-coordinate is an injective encoding.
|
||||
self.cm_full_point(params).into_xy().0
|
||||
}
|
||||
}
|
@@ -1,7 +1,7 @@
|
||||
//! Abstractions over the proving system and parameters.
|
||||
|
||||
use pairing::bls12_381::{Bls12, Fr};
|
||||
use sapling_crypto::{
|
||||
use crate::{
|
||||
jubjub::{edwards, fs::Fs, Unknown},
|
||||
primitives::{Diversifier, PaymentAddress, ProofGenerationKey},
|
||||
};
|
||||
@@ -75,7 +75,8 @@ pub(crate) mod mock {
|
||||
use ff::Field;
|
||||
use pairing::bls12_381::{Bls12, Fr};
|
||||
use rand_os::OsRng;
|
||||
use sapling_crypto::{
|
||||
|
||||
use crate::{
|
||||
jubjub::{edwards, fs::Fs, FixedGenerators, Unknown},
|
||||
primitives::{Diversifier, PaymentAddress, ProofGenerationKey, ValueCommitment},
|
||||
};
|
||||
|
@@ -3,7 +3,7 @@
|
||||
|
||||
use ff::{Field, PrimeField, PrimeFieldRepr};
|
||||
use rand_core::RngCore;
|
||||
use sapling_crypto::jubjub::{
|
||||
use crate::jubjub::{
|
||||
edwards::Point, FixedGenerators, JubjubEngine, JubjubParams, Unknown,
|
||||
};
|
||||
use std::io::{self, Read, Write};
|
||||
@@ -210,7 +210,8 @@ mod tests {
|
||||
use pairing::bls12_381::Bls12;
|
||||
use rand_core::SeedableRng;
|
||||
use rand_xorshift::XorShiftRng;
|
||||
use sapling_crypto::jubjub::{edwards, fs::Fs, JubjubBls12};
|
||||
|
||||
use crate::jubjub::{edwards, fs::Fs, JubjubBls12};
|
||||
|
||||
use super::*;
|
||||
|
||||
|
@@ -3,7 +3,7 @@
|
||||
use ff::{BitIterator, PrimeField, PrimeFieldRepr};
|
||||
use pairing::bls12_381::{Bls12, Fr, FrRepr};
|
||||
use rand_core::{CryptoRng, RngCore};
|
||||
use sapling_crypto::{
|
||||
use crate::{
|
||||
jubjub::{fs::Fs, FixedGenerators, JubjubBls12},
|
||||
pedersen_hash::{pedersen_hash, Personalization},
|
||||
primitives::Note,
|
||||
|
@@ -3,7 +3,7 @@
|
||||
use ff::Field;
|
||||
use pairing::bls12_381::{Bls12, Fr};
|
||||
use rand::{rngs::OsRng, seq::SliceRandom, CryptoRng, RngCore};
|
||||
use sapling_crypto::{
|
||||
use crate::{
|
||||
jubjub::fs::Fs,
|
||||
primitives::{Diversifier, Note, PaymentAddress},
|
||||
};
|
||||
@@ -534,7 +534,8 @@ impl<R: RngCore + CryptoRng> Builder<R> {
|
||||
mod tests {
|
||||
use ff::{Field, PrimeField};
|
||||
use rand::rngs::OsRng;
|
||||
use sapling_crypto::jubjub::fs::Fs;
|
||||
|
||||
use crate::jubjub::fs::Fs;
|
||||
|
||||
use super::{Builder, Error};
|
||||
use crate::{
|
||||
|
@@ -1,7 +1,7 @@
|
||||
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
|
||||
use ff::{PrimeField, PrimeFieldRepr};
|
||||
use pairing::bls12_381::{Bls12, Fr, FrRepr};
|
||||
use sapling_crypto::jubjub::{edwards, Unknown};
|
||||
use crate::jubjub::{edwards, Unknown};
|
||||
use std::io::{self, Read, Write};
|
||||
|
||||
use legacy::Script;
|
||||
|
@@ -1,7 +1,8 @@
|
||||
use ff::Field;
|
||||
use pairing::bls12_381::Bls12;
|
||||
use rand_os::OsRng;
|
||||
use sapling_crypto::jubjub::{fs::Fs, FixedGenerators};
|
||||
|
||||
use crate::jubjub::{fs::Fs, FixedGenerators};
|
||||
|
||||
use super::{components::Amount, sighash::signature_hash, Transaction, TransactionData};
|
||||
use legacy::Script;
|
||||
|
@@ -1,5 +1,6 @@
|
||||
use blake2b_simd::Params;
|
||||
use sapling_crypto::jubjub::{JubjubEngine, ToUniform};
|
||||
|
||||
use crate::jubjub::{JubjubEngine, ToUniform};
|
||||
|
||||
pub fn hash_to_scalar<E: JubjubEngine>(persona: &[u8], a: &[u8], b: &[u8]) -> E::Fs {
|
||||
let mut hasher = Params::new().hash_length(64).personal(persona).to_state();
|
||||
|
@@ -4,7 +4,8 @@ use byteorder::{ByteOrder, LittleEndian, ReadBytesExt, WriteBytesExt};
|
||||
use ff::Field;
|
||||
use fpe::ff1::{BinaryNumeralString, FF1};
|
||||
use pairing::bls12_381::Bls12;
|
||||
use sapling_crypto::{
|
||||
|
||||
use crate::{
|
||||
jubjub::{fs::Fs, FixedGenerators, JubjubEngine, JubjubParams, ToUniform},
|
||||
primitives::{Diversifier, PaymentAddress, ViewingKey},
|
||||
};
|
||||
|
Reference in New Issue
Block a user